Nesneye Dayalı Programlama (OOP) S.O.L.I.D ilkeleri

Bu aralar işlerden fırsat bulup araştırmalar yapıyorum. Belli bir konu üzerinde araştırma yaparken özellikle araştırmayı internet üzerinden yapıyorsanız dallandıkça dallanıyorsunuz. Konu ile ilgili bir makale okurken, makale içinde geçen bir bağlantıya tıklayarak veya makale içinde geçen bir kısalmanın anlamını arayarak ana konudan diğer konulara geçiş yapıyorsunuz.

Son zamanlarda tasarım kalıpları (desing patterns) üzerinde çalışmaktayım. Benim gibi sürekli pratik ile ilerleyen birisi için, kitab-i bilgilere girmek devrim sayılabilir. Tasarım kalıpları kısaca herhangi bir programlama dilinden bağımsız, bir uygulama tasarımında hangi yolların izlenebileceğini, yaparken nelere dikkat edilmesi gerekebileceğinden bahseden konular bütünüdür. (Çok ilginç bir tanım oldu inşallah doğrudur) Özellikle belli başlı tasarım kalıplarını incelerken S.O.L.I.D ilkeler adlı bir konuya daha doğrusu kısaltmaya denk geldim ve dallanarak SOLID 'in ne olduğunu araştırdım.

"SOLID Principles" (katı ilkeler) olarak geçen bu konu aldığı isim ve anlattığı konunun uyuşmasıyla muhteşem olmuş diyebilirim. Nedir bu S.O.L.I.D ilkeler? Nerede kullanılır? Nasıl yapılır?

Aslında kurumsal uygulama geliştiren, uygulama geliştirirken biraz nesneye dayalı programlama tekniklerini kullanmaya çalışan her yazılım geliştiricinin haberi olarak ve/veya olmadan uyguladığı bazı tekniklerdir. SOLID ilkeleri aşağıdaki gibi açıklayabiliriz.
  • Single Responsibility Principle (Tek sorumluluk)
  • Open / Close Principle (Açık / Kapalı olma durumu)
  • Liskov Substitution Principle
  • Interface Segregation Principle (Arayüz Ayırma)
  • Dependency Inversion Principle (Bağımlılıktan kurtarma)
Single Responsibility Principle (SRP) : Her sınıf tek bir amaca hizmet etmelidir. Genel kural olarak yazılan her bir sınıfın sadece yazıldığı amaca hizmet etmesi diğer sınıfların işlerine veya kendi işlemlerinin dışında bir işlemi gerçekleştirmemesi istenir. Basit bir örnekle dört işlem adında bir sınıf hazırlıyorsanız, içerisinde dört işlemi yapan fonksiyon, değişken tanımlamanız gerekir. Kök alma, faktöriyel, kombinasyon, permutasyon... gibi işlemler de dört işlemle yapılıyor diye aynı sınıfın içerisine yazmamanız gerekir. Sınıf adına uygun şekilde işini gerçekleştirmelidir. Aşağıdaki gibi bir örnek de verebiliriz.


Dikdortgen sınıfına ait AlanHesapla ve CevreHesapla sınıfın kendi işlevidir. CizimYap fonksiyonu ve fonksiyona ait değişkenler her ne kadar Dikdortgen sınıfına hizmet ediyor gibi görünse de aslında genel bir çizim fonksiyonu olabilir ve ikinci kısımda belirtildiği şekilde ayrı bir sınıfta değerlendirilmelidir.

Open / Close Principle (OCP): Yazılım içerisinde kullanılan varlıklar (sınıf, fonksiyon, modül gibi) değime kapalı, genişletmeye açık tutulmalıdır. Yani her fonksiyonel değişimde uygulamanın varlıkları değişime özel değişmemeli aksine ek özellik olarak genişletilebilmelidir. Konu nesneye dayalı geliştirme olunca miras özelliğinden faydalanarak absract, virtual, protected sınıflar, fonksiyonlar, değişkenlerin kullanımı yapılması gerekir. Örnek olarak aynı sınıfları kullanacak farklı projeler için, sınıflar her projeye özel değiştirilmemeli, aksine her projede miras alınarak yapabilecekleri genişletilmelidir.


Liskov Substitution Principle (LSP):
İngilizce açıklamalarında anlaşılması zor bir ilkedir. Genel olarak türetilen bir sınıfta, ana sınıfın fonksiyonlarını kullanırken beklenti ve sonuç ilişkisinin doğru kurulması istenir. Türetilecek sınıf için fonksiyonların nasıl sonuçlar çıkaracağını ve ne çıkarması gerektiğini önceden detaylı düşünülmesi gerektiğini belirtir. Popüler örnekle açıklayacak olursak;


Kare sınıfı aslında bir dikdörtgendir. Dolayısıyla kare sınıfı dikdörtgen sınıfından türetilebilir. Yukarıda yazılan kod doğru gibi görünse de test kodunda beklenen sonuç ile gerçek sonuç farklılık göstermektedir. Bu hata Kare sınıfının Dikdörtgen sınıfından en azından bu şekilde türetilemeyeceğini gösterir ve bu türemenin yapılabilmesi için daha fazla düşünmenin gerekliliği ortaya çıkar.

Interface Segregation Priciple (ISP): Nesneye dayalı programlamada kullanılması gereken arayüzlerin (Interface - grafik arayüz "GUI" değil) içerdiği metotlarda gereksiz yoğunluğun azaltılması istenir. Arayüzü kullanacak programcıların, kullanmayacakları metotlara zorlanması istenmez. Bu sebeple üretilen arayüzlerin küçük kapsamlı olması ve nokta atışlı fonksiyonlar içermesi istenir.

Örneğin DB adlı arayüz veritabanı işlemleri için oluşturulmuşsa, "SelectByName" ve "UpdateByName" fonksiyonları zorunlu olmamalıdır. Çünkü bu fonksiyonlar "Name" kolonu içeren fonksiyonlar için çalıştırılmalı, kolonu içermeyen tablolar için veya içermese de başka bir fonksiyonu gerçekleştirmek için kullanılmamalıdır. arayüz programcıyı sadece her sınıf için gerekecek fonksiyonların oluşturulmasına zorlamalıdır.

Dependency Inversion Principle (DIP): Aslında çıkış noktam bu konuydu. Dependency injection ve Inversion of control konularını incelerken karşıma SOLID yapısı çıktı. Bu ilke yüksek seviyedeki modüllerin, düşük seviyedeki modüllere bağımlı olmaması gerekliliğini belirtir. Bağımlılık sadece soyutlamalar (abstractions) üzerinden yapılmalıdır. Ayrıca bu bağımlılıklar detay içermemeli, aynı şekilde soyut kavramlara bağlanmalıdır. Tanım bu şekilde ama sanki biraz soyut kalıyor. Örneğe geçecek olursak;

Personel sınıfı için kaydetme fonksiyonu oluşturmak istediğimizde (Personel üst sınıf) kaydetme şeklinin uygulama içerisinde üç farklı seçenekle yapıldığını düşünelim. Çalışma anında kaydetme şekli belirlenecek ve personel o şekilde kaydedilecekse yazdığımız sınıftaki gibi bir çözüm üretebiliriz. Fakat bu sınıfta SOLID ilkelerinin birincisi olan sınıfa ait tek sorumluluk ilkesi çiğnenmiş oldu. Çünkü Personel sınıfı içerisine dosyaya, log'a ve veri tabanına yazma fonksiyonlarını getirdik. Ayrıca kaydetme fonksiyonu da gereksiz olarak büyüdü.

Sonuç olarak Personel sınıfı hem tek sorumluluğu içermedi hem de kaydetme fonksiyonlarına bağımlı bir yapıya büründü. İleride yeni bir kaydetme türü için Personel sınıfı içerisine yeni bir fonksiyon eklenmeli ve mevcut (Save) fonksiyonda değişiklik yapılmalıdır. Değişiklik yaparak ikinci ilke "Açık / Kapalı durumu" (OCP) çiğnenmiş olur.

Çözüm olarak farklı bir tasarıma gitmemiz gerekir. Personel sınıfı kaydetme fonksiyonunu içermeli, nasıl kaydedeceğini bilmemelidir. Ayrıca kaydetme seçenekleri yeni sınıflar sayesinde arttırılabilmeli ve bundan Personel (üst sınıf) haberi olmamalıdır.
Örnekte Personel sınıfının kaydet metodu bir arayüz (soyut) kullanarak kaydetme işlemini gerçekleştirir. Nasıl gerçekleştireceğinden haberi yoktur. İşlemi "Saving" adlı arayüz (interface) üzerinden gerçekleştirir. Bu sayede SOLID için birinci ilke (SRP) tamamlanmış olur. Üç farklı kaydetme işlemi için, üç farklı sınıf oluşturulmuştur. Bu sayede çalışma anında seçilecek tür ile kaydetme fonksiyonu gerçekleştirilir.

Artık Personel sınıfı tek sorumluluk taşıyan ve bağımlılıkları ortadan kalkmış bir sınıftır.



2 yorum

deneme

Reply

Güzel bir yazı olmuş teşekkürler, ancak OCP prensibindeki örneği biraz daha açıp anlatabilir misiniz ?

Reply

Yorum Gönder