Örneklerle Solid Prensipleri

Merhaba,

Bugün size daha önce yazılım terimleri 01 yazısında kısaca bahsettiğim, solid prensipleri kavramı hakkında daha detaylı bilgi verecek ve örneklerle basitçe anlatmaya çalışacağım. Girizgahı yaptığımıza göre hadi başlayalım. ( Bundan sonrası için kahve eşliğinde devam etmeniz önerilir 😀 )

Solid prensipleri kısaca nesne yönelimli programlama (OOP) yapılırken daha esnek, gelişime açık, temiz kod mantığına uygun geliştirme yapılmasına olanak sağlayan evrenselleşmiş kurallardan oluşan prensiplerdir.
İpuçuGenelde mülakatlar da bu soru karşınıza çıkar!

Solid prensipleri 5 madde altında toplanır.

S Single Responsibility Principle (SRP): Nesnenin sadece bir sorumluluğu olmalıdır.
OOpen/Closed Principle (OCP): Nesne genişlemeye açık, değişikliklere kapalı olmalıdır.
LLiskov ‘s Substitution Principle (LSP): Programdaki nesnelerin, programın çalışmasında sorun yaratmadan kendine alt örnekleri ile değiştirilebilir olmasıdır. Bu aynı zamanda sözleşmeyle tasarım (Design by Contract ) olarak bilinir.
IInterface Segregation Principle (ISP): Nesnelerin ihtiyaç duymadıkları Interface’lerinden mümkün olduğunca ayrıştırılmasıdır.
DDependency Inversion Principle (DIP): Yüksek seviyeli sınıflar, düşük seviyeli sınıflara bağlı olmamalı, her ikisi de soyut kavramlara bağlı olmalıdır.
( Evet maddeleri ilk yazımdan direk kopyaladım 😀 )

Şimdi hepsiyle ilgili basit birer örnek yaparak daha somut şekilde ele almaya çalışalım.

1. Single Responsibility Principle (SRP): Nesnelerin yalnızca tek bir sorumluluğu olmalıdır. Bunun nedeni geliştirmenin daha modüler olması, değişikliğin daha kolay yapılabilmesi, daha yönetilebilir bir kod ortaya çıkarmasıdır. Mümkün olduğunca isterler analiz edilerek parçalara ayrılmalı ve çözüm için yalnızca bir sorumluluğu olan fonksiyonlar kullanılmalıdır. Kısaca tek bir metot altında yüzlerce işlem ve yüzlerce model manipülasyonu olmamalıdır. Bu prensibi uygulamaya çalışmak hem gereksinimlerin iyi analiz edilmesini hem de ileride yapılacak geliştirmeler de ilgili nesneyi bulmayı ve değişiklik yapmayı kolaylaştıracaktır.

Örneğin, iki sayıyı toplamak isteyelim fakat önce sayılar 0 dan küçük mü diye bakalım, herhangi biri küçükse hata , değilse işlemin sonucunu döndürelim.

Önce .net core projemizi oluşturalım. Ben bunun için cmd / komut satırı kullanacağım.

Dosyalarımızı oluşturalım ve daha sonra içlerine girerek 5 madde için de yukarıda ki kalıp ile projemizi açalım. Ben projemi vsCode ile açacağım.

Yukarıda ki komutları genel bir bilgi olması için yazdım. Bundan sonra ilgili sınıfları paylaşacağım. Classlar bölünebilir fakat ben örneğin daha iyi anlaşılması için ilgili classları aynı dosyada kullanacağım. Örnek üstünden devam edelim.

Birde bunun prensip uygulanmış haline bakalım.

Şimdi şöyle düşünelim validasyon kuralımızın mantığı değişti. Prensip uygulanmamış sınıfımızda bunun için ana class da değişiklik yapmamız gerekirken prensip uygulanmış class da yalnızca ilgili metodu değiştirmemiz yeterli olacak.

2. Open/Closed Principle (OCP): Klasik cümle ile nesneler genişlemeye açık değişikliğe kapalı olmalıdır. Bu cümle ilk okunduğunda tam olarak anlaşılamayabilir. Bu yüzden somut bir örnek üzerinden daha anlaşılır bir şekilde anlatmaya çalışacağım. Örneğimiz de toplama yanında çıkarma da yapmak istediğimizi düşünelim. Bunun için eski tarzda koda dokunarak değişiklikler yapmamız, birden fazla yere dokunmamız gerekecek. Buna ek olarak çarpma işlemi yapmak istediğimizde de yine aynı işlemleri gerçekleştirmek zorunda kalacağız.

Gördüğünüz gibi yeni bir işlem için bile ne kadar çok noktaya dokunup, değişiklik ve ekleme yaptık.Yeni bir işlemler geldiğinde bunları tekrarlamamız gerekecek. Şimdi bu işlemleri open-closed prensipine uygun hale getirelim.

Kodumuzu daha genişlemeye açık bir hale getirdik. Yeni bir işlem daha gelse bile yapmamız gereken sadece IOperation interface sinden türeterek Operation methodunu işleme göre düzenlemek.

3.Liskov ‘s Substitution Principle (LSP): Alt sınıflar türetildikleri sınıfların nesneleriyle yer değiştirdiğinde aynı davranışı sergilemeli yani türetildikleri sınıfın tüm özelliklerini kullanmalıdır. Bu prensip bize base classlarda gereksiz kodların olmaması gerektiğini söyler. Şimdi Player dan türeyen iki sınıfımız olsun ve bu sınıfta KickTheBall ve KeepTheBall adında iki metod olsun. Burada prensip uygulanmaz ise base class da gereksiz kodlar bulunacak.
Bu yüzden bu gibi durumlarda base class da türeyen sınıfların hepsinin kullanacağı özellikleri tutmalı, spesifik durumlar için ise ayrı class yada interface üzeriden ilerlemeliyiz.

Gördüğünüz gibi burada striker aslında ihtiyaç duymadığı KeepTheBall metodunu barındırmakta ve normal şartlarda bu fonksiyonu kullanamayacak, bu fonksiyonda exception fırlatması gerekecektir. Yani aslında gereksiz bir kod kalabalığı ve kod yönetimi açısından ek bir efor oluşacaktır, bunun nedeni base classda aslında gereksiz bir metod bulunmasıdır. Aşağıdaki gibi bir yapı kurulması daha sağlıklı olacaktır.

Yani sözün özü base class da ortak olarak kullanılmayacak özellikleri barındırmayın.

4. Interface Segregation Principle (ISP): Nesnelerin ihtiyaç duymadıkları interface lerinden münkün olduğunca ayrıştırılmasıdır. Eğer interfacelerin tüm metodları implemente edilen sınıfta kullanılmıyorsa interfaceleri bölmek gerekir. Şimdi ISuperHero adında bir interfacemiz olduğunu düşünelim. Bu interface de Costume,Power,BatMobile ve Fly adında 4 metod bulunsun ve süper kahraman özelliği olan iki karakter Batman ve Süperman bu yetenekleri alsın.

Fakat burada bir problem var. İkisi de kostüm kullanır,güçleri vardır ama batman uçamaz ve süperman batmobile kullanmaz. O yüzden interfaceler ayrıştırılarak aşağıdaki hale getirilir. Böylece interfacelerde gereksiz özellikler barındırılmamış olur.

5. Dependency Inversion Principle (DIP): Yüksek seviyeli sınıflar, düşük seviyeli sınıflara bağlı olmamalı, her ikisi de soyut kavramlara bağlı olmalıdır. Örneğin validasyon yapan bir sınıfımız olsun. Toplama için sayılardan herhangi biri için 0 dan küçük mü? Bölme için ise herhangi biri 0 mı diye kontrol etsin?
Burada aşağıdaki gibi bir yapı oluşturulursa, düşük seviyeli sınıflara bağımlı bir yapı oluşturulmuş olur. Yani aşağıda ki validation classı bi anlamda SumValidation ve DivideValidation sınıflarına bağımlı haldedir.

Peki aşağıda ki gibi bir yapı kursak ve validation sınıfımız düşük seviyeli sınıflara bağımlı olmasa daha güzel olmaz mı?

Evet arkadaşlar, yazılım ile ilgilenenlerin bilmesi gerektiğini düşündüğüm temel konulardan birini detaylandırdık. Umarım faydalı olmuştur. Yakın zamanda tasarım kalıpları (design patterns) hakkında da bir seri yapmayı düşünüyorum. Takipte kalın!

Altta ki linkleri incelemenizi tavsiye ederim. (Ben inceledim ve bu yazıda yararlandım.)
http://tarikkaygusuz.com/post/solid-prensipleri
http://aliozgur.net/2018/03/02/solid/

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir