DIP – IoC – DI ve IoC container kavramları

Daha önce SOLID prensiplerini anlatırken bahsettiğimiz Dependency Inversion Principle yani bağımlılıkların tersine çevrilmesi prensibinin nasıl gerçek örnekler üzerine implement edileceğinden bahsedeceğim.  Bu prensip altında kullanılan ve birbirleri ile karıştırılan konuların üzerinden geçeceğim.

İyi bir yazılım sistemi geliştirirken kullandığımız elemanlar birbirlerine az bağımlı (loosely coupled) olmalıdır. OOP ile geliştirdiğimiz nesneler esnek olmalıdır ve birbirlerine az bağlı olup değişimlerden az etkilenmelidir. Sistem reusable olmalıdır, kodlar ise encapsulated olacak şekilde geliştirilmelidir. Herhangi bir component diğer  bir component’in ne yaptığı ile ilgilenmemelidir.

En Kullanışlı 2 yöntem abstraction ve interface yapıları ile inşa etmektir. Eğer componentler ileride değişikliğe uğrayacak ise onları concrete tanımlamak yerine, interface yardımı ile oluşturmalıyız.

Aşağıdaki resme bakarak genel kavramlar daha iyi netleşmektedir.

image2

Dependency Inversion Principle (DIP): Yazılım geliştirirken kullanılan SOLID prensiplerinden biridir. Bir sistemin nesnelerinin birbirlerine olan bağımlılıkların az olması için yapılması gerekenleri anlatır. Ancak nasıl gerçek uygulamalar üzerinde uygulanacağını (problem çözümünü) anlatmaz. Bize tanımlama yapar.

Yüksek seviyeli componentler tanımlama yapar ve alt seviyeli componentler onlara bağlı olur. Yani üst componentler alt componentlere bağlı olmaz. Böylece tam tersi olur, bağımlılıklar ters çevrilerek sistem esnek olur. Hem üst seviye, hem de alt seviyeli componentler abstractionlara (soyutlamalara) bağlı olur.

“Abstractions should not depend on details, Details should depend on abstractions.”

Özet olarak DIP prensibi bize sistemin loosely coupled, bağımsız, modüler ve test edilebilir olması gerektiğini söyler.


Inversion of Control (IoC): DIP prensibini uygulayan tasarım şablonudur. Herhangi bir yapıyı yöneteni invert etmektedir. Sınıf veya sistem içerisindeki herhangi bir modülün kullanacağı bağımlılıkları dışarıdan üretip kullanması gerektiğini söyler. Soyutlama yapmamızı sağlar.

Eğer alt seviyeden bağımsız bir üst seviye component oluşturmak istiyorsak, alt seviye componentlerin nesne oluşturmaması için kontrolü invert edip nesne oluşturma işini üst seviye componentler tarafından yapılmasını sağlamalıyız.

IoC yapmak için 3 yol  bulunur. (Yukarıda olan şemada 2 yol bulunuyor.)

  • Interface inversion  (Interfaceler invert ediliyor.)
  • Flow inversion (Akışı invert et)
  • Dependency creation & binding (kendi içinde 3’e ayrılır. en çok kullanılan yöntem DI)

Dependency Injection (DI): IoC pattern’ı uygulamak için yapılmış implementasyondur. Bağımlı nesneleri oluşturma ve bağlama işini sınıfın içinde değil dışarıda yapmaktadır.

Normal şartlarda herhangi bir sınıfın kullanacağı sınıflar, o sınıfın içerisinde tanımlanır. Böylece sınıf, oluşturulan nesnelere bağımlı olur (Tightly coupled). Dependency injection kullanarak bu bağımlı nesneler dışarıda oluşturulur. 3 tane yolu vardır. Kendimiz DI yönetimi oluşturabileceğimiz gibi hazır olarak kullanılmaya hazır IoC framework yapılarından birini kullanarakta yapabiliriz.

  • Constructor Injection: Sık kullanılan bir DI yoludur. Dışarıda oluşturulan bağımlılıkları, aktarılmak istenen sınıfa constructor üzerinden göndeririz. Repository sınıfı içerisinde sadece Save() metodu olan boş bir sınıftır ve IRepository interfaceden türetilmiştir. Main içerisinde oluşturulan repository nesnesi, transaction nesnesine constructor ile enjekte ediliyor. Dbrepository yerine farklı bir repository bağlasak bile transaction sınıfı bağımlı olmadığından sıkıntı olmayacaktır. Çünkü transaction sınıfı içerisinde repository interface ile tanımlanmıştır.

dip1

dip2

  • Setter Injection: Sınıf içerisinde bağımlı sınıfı örneklemek için kullanılacak bir değişken tanımlanır. O değişken set edilerek bağımlılık enjekte edilir. Transaction içerisinde tanımlanmış Repository değişkeni main içerisinde nesne tanımlanırken set ediliyor.

dip3

dip4

  • Interface Injection: Fazla kullanılan bir yol değildir. Inteface içinde metod oluşturulur ve bağımlılık o metod ile aktarılır.

Ioc Container: Dependency Injection yapmak için kullanılan frameworklere denir. Nesne oluşturmasını denetler ve nesnelerin yaşam döngülerini kontrol eder. Sistem çalıştığı zaman ihtiyaç olan bağımlı nesneleri doğru şekilde üretir. C# ile kullanılan IoC containerlar aşağıda gösterilmiştir.

Kaynakça

Advertisements

Adapter Design Pattern

Daha önce planladığım ancak yazmayı ertelediğim tasarım şablonlarına devam ediyorum. Yazmaya başladığım şablon structural(yapısal) tasarım desenlerinden olan adapter tasarım şablonudur. Kısaca tanımını yapalım.

Adapter design pattern ile genellikle sistemimize uymayan sınıfları veya nesneleri, sistemimize adapte etmek için kullanırız. Çevremizde bir çok sistem adaptör mantığını kullanılarak birbirleri ile etkileşir. Örneğin hafıza kart adaptörleri; hafıza kartını bilgisayarımıza direkt olarak takamazsak adaptör kullanarak takarız ve işlemimizi gerçekleştiririz. Şarj için kullanılan adaptörler de bu kapsama girer. Yazılım geliştirme süreçlerinde de aynı mantık geçerlidir. Sistemimize yeni eklemek istediğimiz özellikleri barındıran arayüzü uyumsuz sınıfları veya nesneleri bu şablonu kullanarak sistemimize uyumlu hale getirmekteyiz. 3.parti kütüphaneleri projemize uyumlu hale getirmek gibi.

Temel olarak 2 uyumsuz arayüz arasında köprü görevi görür. 2 çeşit adapter pattern uygulaması vardır. Birincisi ve aşağıdaki uml’de gözüken sistem sınıf adapter yapısıdır. Miras tabanlı adapter işlemi yapar. Diğeri ise object tabanlı adapter yapısıdır. Bu yapı delegate yolu ile işlem yapar.

  • ITarget : ITarget arayüzü client tarafında kullanılan işlemleri gerçekleştirir. Kullanılan ana arayüz sınıfıdır.
  • Client : ITarget arayüzü ile etkileşime girmektedir. Adaptee edilmek istenen sınıf ile uyumsuzdur.
  • Adaptee : Sisteme eklemek istenilen özelliklerin bulunduğu arayüz sınıfıdır. ITarget arayüz sınıfı ile uyumlu değildir.
  • Adapter : Adaptee arayüzünü ITarget arayüzü ile bağlamaktadır. İçerisinde adaptee nesnesini barındırır. Adaptee yapısında olan özellikleri ve metodları adaptee üzerinden sağlamaya yaramaktadır.

Adapter tasarım şablonunu kullanarak basit bir örnek sistemi inceleyelim.

1- IEmployee sınıfı sistemde kullanılan ana arayüzdür. Void tipli salary metodu tanımlanmıştır.

1

2- Employee sınıfı bu arayüzü uygulamaktadır. Salary metodu içerisinde her çalışanın maaş değeri, constructordan dönen isim değeri eklenerek ekrana yazılır.

2

3- Manager sınıfı (adaptee) sisteme eklenmek istenmektedir. Bu sınıfta SalaryBonus metodu bulunmaktadır. Bu metod kullandığımız arayüze uymamaktadır.

3

4- EmployeeAdapter bizim adapter sınıfımızdır. Ana arayüzü ve eklenecek olan(adaptee) sınıfını uygulamaktadır.

4

5 – Çalışan Listesi oluşturuluyor. İki elemean normal employee sınıfından oluşturulmaktadır. Diğer eleman ise adapter sınıfından oluşturuluyor. Show metodu ile tüm çalışanlar listelenmektedir.

5

6 – Ekran görüntüsü aşağıdaki gibidir. John ve Jane adlı çalışanların maaş bilgisi gösterilmektedir. Mark adlı managerin maaş bonusu gösterilmektedir.

6

Adapter design pattern kullanarak yapılmış basit bir örneği inceledik. Daha kapsamlı örnekler internette mevcuttur.

 Kaynakça

Abstract Factory Pattern

Creational design patterns kategorisinde bulunan ve çok kullanılan abstract factory design pattern konusunda yazacağım. Uzunca bir anlatımdan ziyade bu tasarım şablonu kullanılmış bir örnek göstereceğim. Kaynakça olarak paylaşacağım linkler detaylı olarak araştırmak için yeterli olacaktır.

 “Provide an interface for creating families of related or dependent objects without specifying their concrete classes.”

Birbiri ile ilişkili veya bağımlı nesneleri onların concrete (somut) sınıflarını oluşturmadan create edilmesini sağlıyor. Tasarım şablonunu uygulamak için  nesnelerin aynı abstract class veya interface ile oluşturulmuş olması gerekmektedir. Nesnelerin arayüzleri veya soyut yapıları ile ilgilendiği için yeni bir nesne eklemek nispeten kolaylaşıyor. Abstract factory sınıf uml yapısı gösterilmiştir;

abstract

 Bu tasarım desenini ev yapımını örnek alarak anlatacağım. Ev oluşturmak için Kapı, duvar ve çatı gibi sınıflar oluşturup bu sınıfları ev fabrikası soyut sınıfı üzerinde tanımlayarak, o soyut sınıfı kullanarak ev yaratılmaktadır.

  1- Kapı soyut sınıfı oluşturduk ve ondan türetilmiş 3 tane sınıf ürettik.

doorobject

2- Aynı şekilde duvar ve çatı için soyut sınıf ve onlardan türetilmiş sınıflar ürettik.

wallandroofobject

3- Evleri üreteceğimiz ev fabrikası soyut sınıfını oluşturduk. Bu soyut sınıfı içersinde kapı, duvar ve çatı sınıflarını üretmesi için soyut metod olarak tanımlama yaptık.

housefactory

4- Ev fabrikası soyut sınıfından 2 tane ev sınıfı (villa ve apartment) ürettik. Oluşturulan bu sınıfların içerisinde ev fabrikası soyut sınıfında üretilmiş soyut metodları override ile ezdik ve gerekli tanımlamaları yaptık.

5- Aşağıda bulunan estate sınıfı ev oluşturmak için kullanılır. Constructor içinde ev fabrikası, kapı, çatı ve duvar için kullanılacak sınıfların isimleri alınarak ev için uygun nesneler oluşturulmaktadır. Gerekli parametreler ile nesne oluşturmaları yapılıyor. Ardından oluşturulan ev tipi ve nesneler console.writeline ile ekrana basılacaktır.

 6- Öncelikle oluşturmak istediğimiz tipinin nesnesini oluşturuyoruz. Ardından ev oluşturma işlemi için estate sınıfı çağrılacaktır. Constructor içine gerekli parametreler yollanıyor. Bu parametreler tanımlanan ev nesnesi,kapı,çatı ve duvar isimleridir.

createabstraact

 7-  Ekran çıktısı aşağıda gösterildiği gibidir.

main

 

Ne zaman kullanmalıyız ?

  • Sistemin nesne üretiminden bağımsız olması gerektiği durumlarda,
  • Aynı arayüz veya soyut yapıdan oluşan bir sistem ise tercih edilebilir.

Kaynaklar: