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

Observer Design Pattern

Behavioral (davranışsal) tasarım kalıplardan olan observer pattern sık kullanılır. One to many ilişkili birden fazla nesneden oluşur. Bir nesne değiştiği zaman, ona bağlı diğer nesnelerde otomatik olarak değişmektedir. Olay (event) bazlı değişimler olmaktadır.

Aslında observer pattern c#’da bulunan event yapısı ile aynıdır.

Subject: Ana nesnedir. Bu nesne içerisinde diğer nesnelerin içinde bulunduğu liste vardır. Yani diğer nesneler(observers) subject’e bağlıdır. Birden fazla observer tek bir subject’e bağlanabilir. Notify() metodu ile observerların içerisinde bulunan update() metodunu çağırır. Attach() metodu ile nesne bağlarız. Detach() metodu ile nesneleri ayırma işlemini yaparız.

Observer: Subject’e bağlıdır. Ana nesnede değişim olduğu zaman observer içerisinde bulunan update() metodu subject tarafından çağrılır. Böylece her observerler meydana gelen değişimden etkilenir.

Yukarıda bahsettiğim subject ve observer nesnelerini metodları aşağıdaki uml şeması ve sequence diyagramı ile gösterilmiştir. Notify() ile mevcut tüm observer nesnelerinin update() metodları çağrılmaktadır. Böylece olan değişimlerden observerlar etkilenir. Concreteobserver nesnesinde, subject tarafından çağrılan update() metodu içerisinde mevcut durum güncellenir. Concretesubject ise değişim olduğu zaman observerlara bildirim yollar.

 

observeruml
uml şeması

sequence diyagram
sequence diyagram

Örnekler: Observer pattern çalışma mantığını örnekler ile anlatalım.

  • Verilerin 3 farklı sunum şekli tanımlanmıştır. Bunlar observer, veri kaynağı ise subject olarak tanımlanmıştır. Subject üzerinde yapılan herhangi bir değişiklik observerlar üzerinde uygulanır. Observer nesneleri veriyi aynı yerden alırlar ancak birbirlerinden haberleri yoktur. Birden fazla observer bulunabilir.

observer-spreadsheet-example

  • Bir diğer örnekte sorular sorulup kullanıcıların cevapladığı yaygın platform quora üzerinden olsun. Sorulan soru subject olsun. Bu soruyu takip eden 13 kişi olsun. Takipçiler observer nesneleri olacaktır. Mevcut soruya yeni bir cevap yazıldığı zaman soruyu takip eden herkes bildirim almaktadır.
  • MVC (model view controller) mimarisi observer pattern için bir örnektir. Modelin(subject), viewlar(observers) ile ayrışmasını sağlar. Model üzerinde herhangi bir değişim olduğu zaman tüm viewlar etkilenecektir.
  • Event management (olay yönetimi) yapılırken kullanılır.

Kısaca tanıtım yaptıktan sonra kod kısmına geçelim. Github üzerinden koda erişebilirsiniz.

Örnekte ürün market ilişkisi observer pattern ile gösteriliyor. Ürünler subject olarak, marketler observer olarak tanımlanmıştır. Ürünlerin fiyatları değiştiği zaman tüm marketler bu değişimden haberdar olacaktır.

Ürünler için abstract olarak Product sınıfı tanımlanmıştır. Bu sınıftan yeni ürünler yani subjectler türetilecektir. Attach() metodu içerisinde shops listesine marketler eklenmektedir. Yani fiyat değiştiği zaman güncellenecek olan observerlar burada bağlanıyor. Detach() metodu ile tam tersi olarak listeden marketler çıkarılmaktadır. Fiyat ve ürün ismi olarak 2 değişken döndürülmektedir.

abstractsubject

  ProductA sınıfı bu abstract sınıftan türetilmiştir. Aynı şekilde birden fazla ürün yani subject oluşturup onlara farklı farklı observerlar bağlayabiliriz.

concretesubject

Concrete observer sınıfı ile subjectlere bağlanacak nesneler oluşuyor. Bu sınıf içerisinde Update() metodu içeren bir interfaceden kalıtım almaktadır. Subject içerisinde bulunan Notify() metodu içerisinde observer içerisindeki update metodu çağrılmaktadır. Böylece ne kadar nesne üretilmiştir ise hepsi değişimden haberdar olacaktır.

observer

Programı çalıştıran kod aşağıda gösterilmiştir. Önce observer ve subject nesnelerini oluşturduk. Ardından subject nesnelerine observerları attach() metodu ile bağladık. Ardından fiyat değişimleri yapınca her bir observer nesnesinin update() metodu çağrılmaktadır.

programcode

Ekran çıktısı

output

Kaynakça