Object Oriented Design (S.O.L.I.D)

S.O.L.I.D.

Robert C. Martin tarafından isimlendirilen 5 prensip sayesinde Nesneye Yönelik tasarım ve programlamanızın kolay bir şekilde genişletilebilmesini ve daha kolay bakımının yapılabilmesine olanak sunar.

Bence bu prensipleri uygulamanında bir dengesi olması gerekmektedir. Herşeyi bu prensibe göre en başından uygulamaya başlarsanız Over Engineering yapmaya başlarsınız daha basit yapacağınız işleri çok çok karmaşık ve gereksiz hale getirebilirsiniz.

İlk başta ana amacınıza yönelip kodunuz belli ölçüde bu prensiplere uyarak geliştirin ama kesinlikle aşırıya kaçmayın. Ne zamanki yeni ihtiyaçlara gerek duymaya başladığınız , sınıflarınız uzadı gitti, kod okunmaz bir hale geldi. Her bileşen birbirine bağımlı hale gelmeye başladı, koda test yazmanız zorlaştı, kodu reuse edemiyorsunuz, işte tam bu aşamada kodu Refactor etmeniz gerekiyor.

  • Ne en baştan over-desing over-engineering yaparak bir sürü gereksiz sınıf ve interface tanımlayın.
  • Ne de zamanı gelince yapmanız gereken kod düzeltmelerini, refactoring işlemini ertelemeyin.

Refactoring Yöntemleri: Refactoring’i ikiye ayırabiliriz.

  • Code Refactoring : Kodun davranışını değiştirmeden basit bir şekilde yapılan kod kalitesini arttırmaya çalışan aktivitelerdir. Risk içermez. Eclipse, IntelliJ, NetBeans kodun üzerinde yapabileceğiniz hazır refactoring yöntemleri çıkmaktadır. Bunun dışında PMD, CheckStyle, FindBug vb..) kod kalitesini düzeltici işlemlerde bulunabilirsiniz.
  • Architecture Refactoring: Koda gelen yeni yetenekler sonucunda veya beklentiler değişir ve eski mimariniz bunları sağlamaz. Bunun birçok nedeni olabilir scalability, extendibility, testibility, robustness, high availability vb… Bu durumda olduğunca business kodlarınızı altyapı kodlarınızdan soyutlamanızı öneririm bu sayede mimarisel değişikliklerden olabildiğince az etkilenirsiniz ve business kodunuz bu değişimden etkilenmez.

Testlerin Önemi:

Kod Refactoring otomatik olarak yapılabildiği için çok nadir hataya sebep olur. Architecture Refactoring yapılırken UnitTest, Test Covorage, Functional Test, Performans testlerinin olması Mimarinizi değişitiriken bir takım yeteneklerin yanlış veya yeterli kalitede çalışmadığının gözden kaçmamasını sağlar.

Design Patterns:

https://medium.com/design-patterns altında anlattığım örüntüler. Aşağıdaki bahsettiğim prensiplerin gerçek hayatta karşımıza çıkan sorunlar için örnek çözüm yöntemleridir.

S. Single Responsibility Princible:

Bir sınıf sadece amacı/sorumluluğu olmalı ve tüm fonksiyonları bu amaç doğrultusunda yazılmalıdır. Birden fazla amacı tek bir sınıf içerisine koymaya başladığınızda sınıfınızın kodunun karmaşıklaşmaya başladığını , her değişiklik ihtiyacını bu sınıf içerisine koymaya başladığınızı görebilirsiniz.

O. Open-Closed Princible:

Sınıflarınızın genişlemeye açık , değişime kapalı olmasıdır. Özetle amacı sisteme yeni bir fonksiyon eklemek istediğimiz zaman bunu mevcut sınıflara dokunmandan AbstractClass/Interface extend ederek gerçekleştirebilmektir. Bu sayede yeni özellik eklemek için mevcut sınıfların güncellenmesine gerek kalmaz.

L. Liskov Substitution Principle:

Tasarım örüntülerindeki Template Method olduğu gibi Child sınıfların türediği Parent sınıfın metodlarının ve tanımının alt sınıflar içinde bir anlamı olması/gerçekleştirimi olmasıdır. Bu sayede Parent için oluşturduğunuz bir metodu bu Parent’dan türeyen tüm Child sınıflarda kullanabilirsiniz.

I. Interface Segregation Principle:

Bildiğiniz gibi bir sınıf birden fazla interface implement edebilir. Burada Interface tanımlarken olabildiğince mantıksal küçük parçalara ayırmakta fayda vardır. Aksi halde bunu implement eden sınıflar arayüzden implement etmemeleri gereken sınıflar içinde implement fonksiyonları yazmak zorunda kalacak ve bu metodların içerisi boş kalacaktır.

Bundan dolayı arayüzleri olabildiğince küçük mantıksal fonksiyonu yerine getirecek şekilde , hangi sınıflar bu interface implement ederi düşünerek tasarlamakta fayda vardır.

Veya ilk baştan kompleks tanımlanmış bu arayüzler daha küçük parçalara bölünerek Refactoring yapılabilir.

D. Dependency Inversion principle:

Bir bileşen/sınıf başka bir bileşeni/sınıfı kullanarak diğer sınıfa işlemler yaptırıyor ise diğer sınıf ile kendisinin belirlediği bir arayüz üzerinden konuşması ve sınıfın kendisine dışardan enjekte edilmesi büyük fayda sağlar. (Dependency Injection). Bu sayede çağıran sınıfın mantığını çok çok sınırlandırarak sadece işine odaklanmış Bileşenler yapabilirsiniz.

Örneğin: Bir sınıfınız var ve işi kendi tuttuğu değişkenlerin state yazmak. yaz() Siz bu yaz fonksiyonunu gerçekleştiren dosyaya yazma sınıfını direk kullanırsanız. Yarın buyara DB/Log/Dosya/Console/Newtork vb.. farklı kanallara yazma imkanını taşıyan gerçekleştirimleri bu sınıfta kullanamazsınız. Halbuki bu işlemi dışardan başka bir mantık karar verirse , sizin yazdığınız sınıfın yapması gereken sadece kendine enjekte edilen bilmediği bir sınıfın yaz() fonksiyonunu çağırmaktan ibaret olacak. Gerisini düşünmeyecek :)