Clean Architecture Kitabından Notlar – Object Oriented Programming Konseptleri ve Dependency Inversion
OOP KONSEPTLERI ve DEPENCENCY INVERSION
Object Oriented programlama çok kullandığımız bir paradigmadır. Peki gerçek anlamıyla object oriented programlama nedir? Fonksiyonlar ve classlar bir araya geldiği zaman object oriented programlama olur mu? Object oriented iki farklı anlamda algılanır.
Birincisi object oriented programlama ikincisi de object oriented design. Object oriented design dünyayı kod ile modellemenin bir yolu olarak ifade edilir. Gerçek dünyada varlıklar arasındaki ilişkiler nasılsa aynı yapı ve aynı hiyerarşi ile bunları kod’a geçirmemizi modellememizi sağlar.
Peki object oriented programlama için yani teknik anlamda bir programlama dilini object oriented yapan nedir? Teknik anlamda bir dil Encapsulation, Inheritance ve Polymorphism özelliklerini destekliyorsa bu dil object oriented bir dildir, diyebiliriz.
1. ENCAPSULATION
Encapsulation, data ve fonksiyonların etrafına cohesive bir line çizmek ve bu line dışarısına data ve fonksiyonları saklamak ya da kontrollü açmaktır. Yukarıda object oriented paradigmasının gerçek dünyayı modellemek olduğundan bahsetmiştik. Peki encapsulation konseptinin gerçek dünya ile maplenen tarafı, gerçek dünyadaki karşılığı nedir? Diyelim ki bir pastaneniz var. Bu pastane müşterinelerine pasta servisi yapıyor ve gelen müşteriler de hangi pastalardan ve içeceklerden istediğini görüp seçebiliyor. Eğer pastane bir açık büfe konseptinde ise ve müşteriler istediklerini kendileri rahatlıkla alabiliyorlarsa burada herhangi bir kısıt yoktur. Yiyecek&içeceklerin yani nesnelerin dışarıya erişim kısıtı olmadan public olarak açıldığını söyleyebiliriz yani encapsulate edilmemişlerdir. Eğer müşteriler taleplerini garsonlarla ve görevlilerle konuşarak ifade ediyor ve siparişleri alıyorlarsa burada ürünlerin dışarıya kontrollü olarak açıldığını yani setter getter metotlarla açıldığını, ilgili ürünlerin aslında raflarda encapsulate edildiğini söyleyebiliriz. Ancak bazı ürünler yahut malzemeler vardır ki bunlar mutfakta sadece o mutfağın içerisinde çalışan görevliler tarafından ulaşılabilir ve dışarıdan bir müşterinin görmesi ya da ulaşması yasaktır gerek de yoktur. Bunlar da tamamen encapsulate edilmiş private nesnelerdir.
Tam da bu noktada access modifierlar yardımımıza erişir. Kısaca access modifierların ne anlama geldiklerini tekrar edelim.
public : Datanız ya da fonksiyonunuz class içerisinden ve dışarısından rahatlıkla hiç bir kısıt olmadan erişilebilir durumdadır.
private : Datanız ya da fonksiyonununuz yalnızca class içerisinden erişilebilir durumdadır. Classın dışarısından herhangi bir nesne üzerinden erişilemez. İlgili sınıfı kalıtan sınıflar dahi bu özellikleri kullanamaz.
protected : Datanız ya da fonksiyonunuz yalnızca class içerisinden ve o classı kalıtan classlar tarafından yani class ve sub classları tarafından kullanılabilir durumdadır. Bunun dışında erişim yoktur.
Internal : Internal tanımlanmış bir nesneye ya da methoda sadece aynı derleyici (assembly) tarafından erişim sağlanabilir. Dışarıdan erişim sağlanamaz. Yani data aynı assembly içerisinde public, assembly dışarısında ise private özellik gösterir.
2. INHERITANCE
Nesne Yönelimli Programlama dillerinde inheritance(kalıtım) olgusu, bir sınıfta (class) tanımlanmış değişkenlerin ve/veya metotların (fonksiyon, procedure) access modifierının izin verdiği ölçüde yeniden tanımlanmasına gerek olmaksızın yeni bir sınıfa taşınabilmesidir. Bunun için yapılan iş, bir sınıftan bir alt-sınıf (subclass) türetmektir.
Madem ki object oriented programlama için gerçek dünyayı modelleme konseptinden bahsettik ve object oriented’ın temel yapı taşlarından birinin de kalıtım oldugunu belirttik bunu yine gerçek hayattan bir örnek üzerinden anlatalım. Çok basit bir mantıkla baktığınızda dikdörtgen ve kare nesnelerinizin olduğunu ve herbirinin kendine ait fonksiyonellikleri olduğunu varsayalım. Ama durun bir dakika! aslında her kare aslında bir dikdörtgen değil midir? Bu sebeple kare dikdörtgenin tüm özelliklerini taşımaz mı? Burada bizim dikdörtgendeki aynı fonksiyonellikleri birebir kareye de tanımlamamız biraz anlamsız olacaktır. Dolayısıyla tek yapmanız gereken kareyi dikdörtgen sınıfından kalıtmaktır. Bu sayede kare sınıfı access modifierların izin verdiği ölçüde dikdörtgen sınıfının tüm özelliklerini kalıtım yoluyla alıp kullanabilecektir.
3. POLYMORPHISM
Çok biçimlilik anlamına gelen polymorphism object oriented’ın en önemli özeliklerinden birisidir. Kısaca bir üst sınıftan türeyen tüm alt sınıflar üst sınıfların yerine kullanılabilir. Basit bir mantıkla yukarıda da bahsini geçirdiğimiz gibi dörtgen isminde bir sınıfınız var ise, bundan türeyen kare ve dikdörtgen sınıflarınız, gerekli noktalarda dörtgeni parametre olarak alan tüm fonksiyonlara entegre olabilip parametre olarak geçilebilirler ve yeni bir dörtgen tipi sisteme dahil olup dörtgen classından türetildiğinde aynı şekilde hiç bir ek işlem yapmadan sisteme dahil olabilir. Polymorphishm object oriented programlamanın en güçlü özelliğidir ve bize plugin mimariler geliştirebilmemizi sağlar.
Peki polymorphism’in gücünden nerede faydalanabiliriz?
Dependency Inversion
Projemizdeki componentleri low level ve high level olarak ayrıştırabiliriz. Hatta hiyerarşi yapısı çok olan projelerde buna middle leveli da ekleyebiliriz. Peki bu kavramlar neyi ifade ediyor. Burada component kavramı bir class olabileceği gibi bir katman hatta bir web api bile olabilir ancak yazı boyunca class’ı baz alarak ilerleyeceğim. Low level classlar diğer tüm classlardan daha bağımsız core, fonksiyonellikleri taşıyan classlar olarak değerlendirilebilir. High level classlar ise bu low level classları kullanarak operasyonlarını gerçekleştiren akışlar yöneten classlardır. Bu yaklaşımla baktığımız zaman high level classlar low level classları call etmeli yani high level classlar low level classlara bağımlıdır diyebiliriz. Hangi classın hangi classın fonksiyonunu call ettiği şema da flow of control olarak adlandırılır. Yine bu mantıkla baktığımızda flow of control de bize bağımlılıkları dikte eder diyebiliriz.
Dependency Inversion da tam da bu noktada devreye giriyor ve söylediği de şu ;
High level component’lerinizi low level compnent’lerinize sıkı sıkıya bağımlı kılmayın. Birbirini consume eden high level ve low level componentler arasına bir interface yerleştirdiğimiz zaman aşağıda da gördüğünüz gibi bağımlılıkların yönü değişecektir. Yani artık bağımlılık yönümüz bize flow of control tarafından dikte edilmiyor.
İşte bu object orientedın gücüdür. Peki bu gücü kullanarak neler yapabiliriz?
Artık source kod dependencyleri üzerinde kontrole sahibiz. Artık high level componentlerimiz low level componentlerimize bağımlı değil. Bunun anlamı yukarıda da söylediğim gibi biz sadece bir interface yani bize açılan arayüz’ü biliriz ve bunun arkasındaki değişiklikler ile ilgilenmeyiz. Bu interfacei implemente eden her türlü class yahut component ile çalışabiliriz.
Bir uygulama yazdığımızı varsayalım. Bu uygulama, uygulamayı uygulama yapan esas business rulellarından ve database operasyonlarından oluşuyor olsun. Normal şartlarda business rulelarını işletirken belli yerlerde database metotlarını çağırmamız gerekir. Dolayısıyla business rulelarınnın bulunduğu component, database componentine bağımlıdır diyebiliriz. Yani business ruleların oldugu component high level, database operasyonlarının bulunduğu component ise daha stabil ve bağımsız olan low level componenttir.
Dependency inversion prensibi gereğince high level componentimizi low level componentimize bağımlı kılmamak adına business componenti ile database componenti arasına bir interface yerleştiririz. Dolayısıyla artık bağımlılıklar flow of control yönünde değil terse dönmüş bir vaziyettedir. Peki bunun sonunda ne elde ettik? Artık business rulelarımız bir database componentine direkt olarak bağımlı değil. Bu fonksiyonları göbekten call etmiyor ve sadece bir arayüz üzerinden haberleşiyor durumda. Bu da bize istediğimiz zaman, arkadaki database componentini değiştirebilme, örneğin SQL kullanırken mongoDB data componenti entegre etme ve bunu da business rule’ın ruhu duymadan yapma imkanı sağlar. Yani kısacası artık high level olan business rule, data componentini call etse bile ona bağımlı değildir.
Sonuç olarak
Bazı kaynaklarda object oriented denen şeyin polymorphismin gücünün kullanabilmek ve source code dependencylerini yönetebilmek olduğu söylenir . Bu da bir architect’e high level policyleri
low level policylerden bağımsız oldugu plugin mimarisi yaratma imkanı sunar.