SOLID Yazılım Prensipleri
Nesne yönelimli bilgisayar programlamada SOLID, yazılım tasarımlarını daha anlaşılır, esnek ve sürdürülebilir hale getirmeyi amaçlayan, Robert C. Martin tarafından öne sürülen beş tasarım ilkesinin anımsatıcı kısaltmasıdır. Bu prensipler şu şekildedir:
- Single-Responsibility Principle
- Open-Closed Principle
- Liskov Substitution Principle
- Interface Segregation Principle
- Dependency Inversion Principle
Şimdi bu prensiplerin detaylarına girelim.
Single-Responsibility Principle
Bu ilk ilke, “bir sınıfın değişmesi için yalnızca bir nedeni olması gerektiğini”, yani tek bir sorumluluğu olması gerektiğini söyler. Kullanmış olduğumuz yapılar (metots, classes, layers …) sadece bir işi yapmalıdır.
Şimdi bu durumu bir örnek üzerinde inceleyelim. Örneğimizde bir çalışanımız olacak. Ve bu çalışanın özelliklerini, veritabanı işlemleri ve yaptığı görevler ile ilgili raporları yer alacak. Bunun için single-responsibility prensibine uygun tasarımımızı gerçekleştirelim. İlk olarak bu prensipe uygun olmayan bir yaklaşımda kodumuzu yazalım.
Burada Employe sınfımızın tek işi özellikleri barındırmak değil. Görüldüğü gibi içerisinde Insert ve GenerateReport metotlarını da barındırmakta. Yani aslında iki farklı sorumluluğumuz daha bulunmakta. Bu durum Single-Responsibilitye uymayan bir durum. Şimdi buradaki yapıyı Single-Responsibilitye uygun hale dönüştürelim.
Open-Closed Principle
Nesneler veya varlıklar genişletme için açık, ancak değişiklik için kapalı olmalıdır. Bu basitçe, bir sınıfın kendisini değiştirmeden kolayca genişletilebilir olması gerektiği anlamına gelir. OOP hakkında genel bir anlayışınız varsa, muhtemelen polimorfizmi de biliyorsunuzdur. Inheritance veya interface implementasyonlarını kullanarak, sınıfların birbirlerinin yerine polimorfik olarak ikame etmesini sağlarız. Bu sayede kodumuzun Open-Closed prensibine uyumluluğu olduğundan emin olabiliriz.
Şimdi bir örnek gerçekleştirelim. Bu örneğimizde geometrik şekillerin alanlarını hesaplayacağımızı varsayalım. Burada şekillerin özellikleri için ayrı sınıflar, hesaplamalar için bir temel sınıf ve şekillerin her birinin alan hesapları için ayrı sınıflar oluşturacağız.
Burada görüldüğü üzere, biz yeni bir şeklin alanını hesaplamak istediğimizde var olan hiçbir koda dokunmadan sadece yeni şeklimiz ile iligili kod bölümlerini ekleyerek projemizi genişletebiliriz.
Liskov Substitution
Liskov Substitution, nesneler için bir ikame edilebilirlik kavramını tanımlar; yani, S, T’nin bir alt türü ise, o zaman bir programdaki T tipi nesneler, o programın istenen özelliklerinden herhangi birini değiştirmeden S tipi nesnelerle değiştirilebilir. Daha genel olarak tanımlarsak, bir programdaki nesnelerin, o programın doğruluğunu değiştirmeden alt türlerinin örnekleriyle değiştirilebilmesi gerektiğini belirtir.
Bir önceki örneğimizde de aslında buradakine benzer bir yapı ortaya koymuştuk. Şuan yapacağımız örnekte ona benzer olacak ama sadece üzerinde duracağımız nokta biraz daha farklı. Burada odaklanmamız gerekken nokta, birbirine benzeyen yapıları aynıymış gibi kullanmamamız gerekiyor. Yani bir karenin alanı ile bir dikdörtgenin alanı benzer şekillerde hesaplanır. Ama bu nesneleri birbirlerinin aynısı gibi düşünemeyiz. Şimdi örneğimize geçelim.
Örnekte de göstermiş olduğum gibi, birbirine benzeyen varlıklarımızı ayrı bir şekilde değerlendirdik.
Interface Segregation
Bu prensip, birçok özel arayüzün(interfaces) genel bir arayüzden daha iyi olduğunu söylüyor. Pek çok davranışa sahip olan arayüzlerin sürdürülmesi ve gelişmesi zor olacaktır. Bunun yerine amaç az sayıda davranış ile ilgilenen yalın arayüzlerin oluşturulmasıdır.
Şimdi bir örnek gerçekleştireceğiz. Örneğimizde tam zamanlı ve sözleşmeli çalışanlarımız yer alacak ve bu kişilerin bilgilerini tutacağız. Burada, çalışanlarımızın benzer özellikleri olacağı gibi farklı özellikleri de olacak. Şimdi prensibe uygun şekilde kodu oluşturalım.
Yukarıda da görüldüğü üzere çalışanlarımız, kendilerine özgü olan özellikleri içeren arayüzlere sahipler ve ortak özellikleri temel bir arayüzden inherit etmekteler.
Dependency Inversion
Nesne yönelimli tasarımda, dependency inversion ilkesi, ayrıştırıcı yazılım modüllerinin özel bir biçimidir. Bu prensibi takip ederken, yüksek seviyeli modüllerden düşük seviyeli bağımlılık modüllerine kadar kurulan geleneksel bağımlılık ilişkileri tersine çevrilir, böylece yüksek seviyeli modüller düşük seviyeli modül uygulama detaylarından bağımsız hale gelir. İlke şunu belirtir:
- Yüksek seviyeli modüller, düşük seviyeli modüllere bağlı olmamalıdır. Her ikisi de soyutlamalara bağlı olmalıdır (örneğin interfaceler)
- Soyutlamalar ayrıntılara bağlı olmamalıdır. Ayrıntılar (somut nesneler/uygulamalar) soyutlamalara bağlı olmalıdır.
Burada yaygın olarak kullanılan tasarım deseni dependency injectiondır. Dependency injection, bir nesnenin (veya statik yöntemin) başka bir nesnenin bağımlılıklarını sağladığı bir tekniktir. Bu tekniği uygulamanın 3 farklı yolu vardır. Bu yollar şu şekildedir:
- Constructor Injection
- Property(Setters) Injection
- Method Injection
Constructor Injectionda bağımlılıkları enjekte etmek için constructor enjeksiyonu parametlerini kullanır.
Property Injectionda bağımlılık bir public özellik aracılığıyla sağlanır.
Method Injectionda bağımlılıklar metotlar aracılığıyla sağlanır.
Şimdi bu üç farklı yol ile ilgili birer örnek gerçekleştirelim.
Constructor Injection
Property Injection
Method Injection