Java – Object Oriented Kavramı IV
Yazılım Tasarım Prensipleri özellikle büyük projelerde gerçekten önem arz ediyor. Proje boyunca en çok üzerinde durulması ve düşünülmesi gereken noktanın burası olduğunu yaşayarak anlamış bulunuyorum. Yazılım tasarımı derken kastettiğimiz ekranların tasarımı gibi birşey değil. Class hiyerarşisi, db yapısı, kullanılacak frameworkler ve bunların entegrasyonu, kullanılacak tasarım şablonları (design patterns) ve bunların uyumu gibi konuları kapsayan bir tasarımdan bahsediyorum. Burada dikkat edilecek pek çok nokta var. Ancak en temel ve objektif olanları yazılım tasarım prensipleri ismini haketmiş ve burada onlardan bahsedeceğiz.
Bu prensipleri üstad Robert Martin’in “Agile Software Development: Principles, Patterns, and Practices” isimli kitabında bulabilirsiniz. Bu fikirlerin ilk olarak toplandığı kitaptır ve çok değerlidir. Bu kitaba göre yazılımınızı tasarlarken 3 şeyden kaçının;
- Esneksizlik – bir değişiklik yapmak çok zordur, genelde küçük bir değişiklik için onlarca yer değiştirilmek zorunda kalınır.
- Kırılganlık – yapılan bir değişiklik yazılımı hiç beklenilmeyen bir yerden patlatabilir.
- Modülersizlik – reuse edilemeyen, modüler olmayan sınıflardan oluşan yazılımdır, çünkü iş kuralları ile altyapı içiçedir.
Evet aynı kitapta bu 3 beyazdan korunmak için tasarım prensipleri şöyle açıklanmıştır;
Açık Kapalı Prensibi
- OOP temel parçaları olan sınıflar, modüller ve methodlar genişletilmeye açık ancak değiştirilmeye kapalı olmalıdır.
Bu genel bir prensiptir. Örneğin bir sınıf yazarken şöyle düşünün; ileride bu sınıfın birşey daha yapmasını istediğinizde onu tekrar yazmak veya değiştirmek zorunda kalmadan genişletebileceğiniz şekilde yazın. Aynı şey modüller veya kütüphaneler içinde geçerli. Örneğin spesific bir iş için birçok sınıftan oluşan bir kütüphane oluşturduğunuzu varsayın. Bu kütüphaneyi kullanan birçok client olacaktır ve siz yeni bir özellik eklemek istediğinizde varolan kodları değiştirirseniz, bu çok kötü sonuçlar doğurabilir. Hiç değişiklik yapmadan varolan olan sınıfları genişleterek işinizi halledebiliyor olmanız gerekiyor.
Peki ama bunu nasıl sağlarız. OOP bizim bu prensibi sağlayabilmemiz için harika bir yapı sunmuş, Abstract Class. Yazılımın, kütüphanenin, modülün veya sınıfın davranışlarını bir abstract class ile belirleyin, daha sonra bu davranışları implement eden concrete class’ları oluşturun. Böylelikle concrete class’larınız hiçbir zaman abstract class’ı değiştiremez sadece genişletebilir. Yani yazılımın temel davranışı değişmez sadece genişler… Bunun örneklerini template ve strategy tasarım desenlerinde görebilirsiniz. Bir başka yazıda bu desenlerden bahsetmeyi umuyorum.
Tersine Bağımlılık Prensibi
- Hiyerarşi ağacında üst seviye sınıflar kendilerinden alt seviye sınıflara bağımlı olmamalıdır. Her biri abstract elemanlara bağımlı olabilir.
- Abstract sınıflar detaylara bağımlı olmamalıdır. Detaylar abstractlara uygun yazılır.
Tersine bağımlılık prensibi, üst seviye sınıflar ile alt seviye sınıfları bağımlılık açısından birbirinden ayırmamızı söyler. Bunu yapabilmek için araya abstract bir katman konur. Yani bağımlılığı tersine çevirmek, detaylara göre abstraction yazmak yerine abstraction’lara göre detay yazmayı sağlamaktır. Zaten abstract kelime anlamıyla özet demektir, önce alt sınıfların yapacaklarını bir özetlersiniz. Daha sonra detaylara inersiniz. Hiyerarşide ne kadar aşağıya inilirse o kadar detay, ne kadar yukarıya çıkılırsa o kadar özet alınır. Hiçbir zaman alt sınıfa uysun diye üst sınıfın methodları detaylandırılmamalıdır.
Tasarım desenlerini anlatırken bu konularla ilgili örnekler sunabileceğimi düşünerek burada örnek vermiyorum. Örnek olmayınca da biraz havada kalıyormuş gibi geliyor. Bu yüzden ilk paragrafta anlatılanları biraz daha açayım. Örneğin bir sınıf başka bir sınıfı kullanma ihtiyacı duyuyor. Klasik olarak düşünürsek o sınıfın bir instance’ını oluşturup kullanırsınız. İlk sınıfın kendi içinde ikinci sınıfa direkt bir referans tutuyor olması bu iki sınıfı tamamen bağlı hale getirir. Yazılımınızda böyle büyük bağımlılıklara izin vermemeye çalışmalısınız. Bunun için ikinci sınıfın üstüne bir abstract katman koyup, birinci sınıfta bu abstract katmana referans tutabilirsiniz. Böylelikle iki sınıf arasındaki bağımlılığı koparmış olursunuz. Değişiklik yapılacağı zamanda sadece abstract katmanda düzenleme yapılarak ilişkilerin yönleri değiştirilebilir. Bu işi yapan tasarım desenlerine genelde factory ya da abstract factory denir. Yani sizin sınıf üreten bir fabrikanız olur ve siz ona istediğiniz sınıfı söylersiniz, o da üretir. Fabrikanın ürünlerine değil fabrikaya bağımlı olursunuz.
Interface Ayırma Prensibi
- Sınıflar yalnızca ihtiyaç duydukları interface’leri kullanmalıdırlar, ihtiyaç duymadıkları interface’leri kullanmamaya zorlanmamalıdırlar.
Bu prensipte kastedilen şudur; uzun ve genel bir interface yazarak birçok sınıfta bu interface’i implement etmek yanlıştır. Bu implement eden sınıfları, gereksiz olsa dahi o interface’deki tüm methodları implement etmeye zorlar. Bunun yerine interface’lerinizi mümkün olduğunca ayırmak ve belli bi işle ilgili özelleşmiş interface’ler yazmak çok daha doğru olacaktır. Interface’i yazdıktan sonrada bi method eklerken o methoda gerçekten o interface’de ihtiyaç duyulup duyulmadığı iyice düşünülmelidir. Örneğin IOgrenci isminde bir interface yazdığınızı düşünün ve bütün Ogrenci sınıflarınız bu interface’i implement ediyorlar. Bu interface’e okulaGit isminde bir method eklemek doğru mudur? Bu methodu eklerseniz bütün öğrenciler okula gitmek zorunda kalacaktır. Ya sisteminizde açık öğretim okuyan öğrenciler mevcutsa?
Özet olarak bir interface’de aslında orada olmaması gereken bir method varsa, o interface kirlenmiştir ve ondan türeyen sınıflarıda kirletebilir. Temizlenmesi gerekir..
Tek Sorumluluk Prensibi
- Bir sınıfın değiştirilmesi için sadece tek bir neden olmalıdır.
Burada kastedilen bir sınıfın sorumlu olduğu tek bir iş olmasıdır. Eğer bir sınıf tek bir işten sorumlu olursa değişmek içinde tek bir nedeni olur, o işin değişmesi… Eğer bir sınıfın değişmesi için 2 sebep bulabiliyorsanız, o sınıf 2 işten sorumlu demektir ve bu prensibe göre o sınıfı 2 fonksiyonaliteyi sağlayan 2 sınıfa ayırmanız gerekir. Böylelikle fonksiyonalitelerden birinde bir değişiklik olduğunda sadece o işten sorumlu olan sınıfı değiştirirsiniz ve diğer fonksiyonalitenin etkilenme olasılığını ortadan kaldırmış olursunuz.
Bu prensip en başta bahsettiğim kitapdan daha önce tanıtılmış olsa da Robert Martin tarafından sorumluluk yerine değişme nedeni konseptiyle tekrar yayınlanmıştır.