Nesneye yönelik(Object-oriented) programlama diyince, eminim bir çok kişi “class”,”abstraction”,”encapsulation”,”inheritance”,”polymorphism” gibi janjanlı kelimeleri söyleyecektir. Kesinlikle yanlış başlıklar ya da konular değil…Nesneye yönelik programlamanın hatta temel taşları…Ama bunları bilmek, nesneye yönelik programlamayı anlamak, bilmek ve faydalanabilmek için yeterli mi?…
Bu soruya benim kendi kişisel cevabım, kesinlikle hayır olacaktır. Yarattığım sınıfları başka sınıflardan türeterek, çeşitli soyutlamalar yaparak, belli metodları ya da sınıfın özelliklerini saklayarak nesneye yönelik programlama yapabilirim ama bu gerçekten nesneye yönelik programlamanın amacına yönelik bir şey mi olur…
Bu sorunun cevabını verebilmek içinde nesneye yönelik tasarımı irdelemeye çalışmak, nesneye yönelik programlama ve nesneye yönelik tasarım arasında ki ilişkiyi anlamak yeterli olacaktır.
Nesneye yönelik tasarım, bir sistemi oluştururken, sistemin parçalarını, nesneler ve bu nesnelerin metodları ve özellikleri ile, birbirleri arasında ilişki sağlayabilmek şeklinde özetlenebilir. Bu noktada SOLID diye kısaltabileceğimiz 5 kavram karşımıza çıkıyor. Bunları ilke ve belki de kural şeklinde de ele alabiliriz, ki bence nesneye yönelik tasarımı daha iyi anlamak adına bu şekilde düşünmekte fayda var.
“Single responsibility”(ayrı sorumluluklar), “Open-closed”(Açık-kapalı),” Liskov substitution”(yerine konulabilirkik), “Interface segregation”(arayüzlerle ayırma) ve “Dependency inversion”(bağımlılıkları ters çevirme) kavramlarının kısaltması olan SOLID, nesneye yönelik tasarımın en temel olmazsa olmaz 5 ilkesidir. Ve nesneye yönelik programlama kavramlarının da aslında ortaya çıktığı noktalardır.
Single Responsibility: Bu ilke, her nesnenin kendine ait bir sorumluluğu olmasını söyler. Nesnelerin sorumlulukları dışına çıkması, tasarımı ve geliştirmeyi zorlayacak bir unsur olacağından, bu ilke nesneye yönelik tasarımın 5 ilkesinden en önemli olanıdır diyebilirim. Bu yüzden tasarım esnasında mimari bileşenlerinizin nesne modellerini iyice biçim tartmak gerekecektir. Oluşturacağınız nesnelerin özellikleri, metodları ve başka nesneler ile olan iletişimlerinin nasıl olacağı gibi konular nesnenin kendine ait bir sorumluluğu olması açısından önemle ele alınmalıdır. Ama tabi geliştirme ve tasarım süreçlerinin aynı ilerlediği(olmaz demeyin, gayette olur, çok da anormal bir durum değildir aslında) uygulama geliştirme durumlarında bu ilke başka nedenlerden dolayıda çok ihlal edilir…Hele ipin ucu kaçtı mı, bir çok şeyden sorumlu nesneler ve hiç bir sorumluluğu olmayan nesneler sistemde dolaşır…
Open-Closed: Bu ilke, nesnelerin değişikliğe kapalı(Closed), ama genişletmeye açık(Open) olmasını söyler. Bir nesnenin kendi özelliklerinin değişmesinden çok, bu değişiklikleri genişletme bakış açısından ele alıp, nesneyi genişletmek, nesnenin yavaş sürecinin sağlıklı kalmasını sağlayacaktır. Bir metodun değiştiğini düşünün, bu metodu kullanan diğer bileşenlerin de mevcut değişikliğe ayak uydurması gerekecektir. Bu bileşen çalışan bir sistem içerisindeki bir bileşense bu değişikliğe ayak uydurmak biraz zor olacak ve hatta çalışma süresine etki edecektir. Bundan dolayı nesneleri değiştirmek yerine genişletmek daha doğru olacaktır. Nesneleri “inheritance” kavramını uygulayarak yeni nesneler yaratacak şekilde genişletmek, ya da belirli “interface”(arayüz) ya da “abstract”(soyut) sınıfları uygulayacak şekilde genişletmek bu ilkenin doğru işletilmesi adına doğru bir yaklaşım olacaktır.
Liskov substitution: Bu ilke, X tipinden türeyen Y tipinde ki bir nesnenin, X tipinden türeyen herhangi bir tip ile değiştirebileceğini söyler. Burada önemli olan bu değişikliği yaparken, bu nesneleri kullanan sistemde bir değişiklik yapılmaması gerekliliği…Yani İnsan sınıfından türeyen Erkek sınıfı ve Kadın sınıfı, İnsan sınıfının özelliklerini, metodlarını kullanan bir sistem içerisinde değiştirilebilmelidir. (Ama tabi ayrımcılıkta yapmamak lazım 😀 )
Bu ilkenin amacı yapılan tasarımın belli kontratlara göre yapılmasını sağlamaktır. Bu sayede geliştirilen sistem kendi içinde istikrarlı ve kurallı olacaktır. Aynı zamanda bu kontratlara uyan başka nesneler ile de sistemin genişletilmesi mümkün olur. WCF deki “Data Contract” kavramının da temelinde bu ilke vardır. Aynı şekilde .NET Framework 4.0 ile beraber gelen “Contracts” kavramını da bu ilkeye örnek olarak gösterebilirz.
Interface segregation: Bu ilke, bir “interface”(arayüz) aracılığıyla yaratılan nesnelerin gerektiğinde başka arayüzlere ayrılması gerektiğini söyler. Aslında amaç olarak Single Responsibility’e benzer, çünkü bu sayede her nesnenin sorumluluğu kadar arayüzü olmasını söyler. Bütün nesneleri bir arayüzden türetmek, bazı yaratılan nesnelerin o arayüz methodlarına ihtiyacı olmamasına rağmen kullanmasını zorunlu kılar. Ama bu çok doğru ve istenen bir kullanım olmamalıdır. Çünkü nesnelerin karmaşıklığını artırır…Bu yüzden nesneleri farklı arayüzlere ayırmak karmaşıklığı azaltır.
Dependency inversion: Bu ilke, nesnelerin soyutlaştırılmasını ve nesnelerin başka nesnelere bağlılığından çok, soyut nesnelere bağlı olmasını söyler. Bu sayede nesnelerin birbirlerine bağımlılıkları azalacaktır. Farklı ihtiyaçlara hitap eden nesnelerin birbirine bağımlılığı, yine nesneleri karmaşıklaştıracaktır. Nesnelerin birbirlerine bağlı olma şekli önemli ama nasıl bağlandığı önemli olmamalıdır. Bu detayların her nesne(leri)nin kendi içinde yönetilmesi daha doğru bir yaklaşım olacaktır. Genellikle bu ilkeyi nesneye yönelik tasarımda uygulamak oldukça zor olabiliyor, en azından benim gözlemlediğim süreçler bu yönde. Ancak bu ilke başarılı bir şekilde uygulandığı takdirde sağlıklı tasarımlar yapmak daha kolay olacaktır.
Nesneye yönelik programlama ve nesneye yönelik tasarım kavramlarını bir birinden ayırabilmek çok önemlidir. Bundan dolayı kendimce, çat bat ikisi arasında ki ilişkiyi biraz özetlemeye çalıştım. Umarım faydalı olur…
Neyse şimdilik bu kadar…Bu ve benzeri konularda biriken yazacak çok şey var…Yazma konusunda ki tembelliğimi yendiğim an onları da burada bulabileceksiniz…