Inversion of Control / Dependency Injection Principle

Orjinal yazar Tatham Oddie… Bu yazıyı Tatham’ın verdiği bir sunumdan
izniyle çevirdim.

Giriş

Inversion of Control (IoC) bir modülün bağımlılık, çözülme, konfigürasyon ve
hayat döngüsü konularına çözüm sağlayan dizayn kuralıdır. IoC’nin en önemli
özelliği bir modülün bağımlı olduğu diğer assembly bileşenlerinin çözülümünde
yatmaktadır.

IoC’nin en iyi bilinen olayı Dependency Injection Principle’dır (DIP). Diğer
bir adıda Hollywood kuralı’dır (Bizi arama, biz sana haber veririz). Bu kural
bir dizayn paterni değildir.

Genel olarak modüllerin gevşek olarak birbirine bağlanmasını sağlar. Gevşek
bağlılığın sağladığı yararları sıralarsak:

  1. Yeniden kullanılabilirliğin arttırılması
  2. Test işlemlerinin kolaylaştırılması
  3. Bir araya getirmesi ve konfigürasyonu kolay sistemler (Modül bazında)

Dependency Resolution

Basitçe özetlersek, IoC kuralına göre dizayn edilmiş bir modül (Modül A),
işlerini tamamlamak için kullandığı diğer modülleri (Modül B, Modül C, Modül D)
yada assembly’leri çağırmaz. Bunun yerine bağımlı olduğu modülleri (Modül B,
Modül C, Modül D) tanımlar ve bir container nesnesi bu tanımlanan modülleri
kullanıma sunar. Yani çağırılan modüllerin kontrolü ters yüz edilir. Modül A
çağırdığı modüllerin (Modül B, Modül C, Modül D) kontrolünü yapmaz, bu işlem
artık container nesnesinin işidir.

Çağırılan modüller 3 şekilde tanımlanabilir.

Constructor Injection – çağırılan modül sınıfın “constructor”
fonksiyonunda “property” olarak tanımlanır ve sınıf nesne olarak
oluşturulduğunda container tarafından doldurulur.


public class SimpleMovieFinder : IMovieFinder

{


private IMovieDatabase database;


public SimpleMovieFinder(IMovieDatabase database)

{


this.database = database;

}


public string[] GetByTheatre(long theatreId)

{


string filter = string.Format(“[TheatreId] = {0}”, theatreId);


return database.GetByFilter(filter);

}

}

Setter Injection – bağımlı modüller “public property” olarak
tanımlanır (get – set) ve “container” sınıfın nesne olarak oluşturulduğu sırada
bu “public property”leri set eder.


public class SimpleMovieFinder : IMovieFinder

{


private IMovieDatabase database;


public SimpleMovieFinder()

{

}


public IMovieDatabase Database

{


get { return database; }


set { database = value; }

}


public string[] GetByTheatre(long theatreId)

{


string filter = string.Format(“[TheatreId] = {0}”, theatreId);


return database.GetByFilter(filter);

}

}

Contextualized Dependency – sınıf bir “service manager”
sınıfından miras alır ve çağırılan diğer modüller bu servis yönetici sınıfı
tarafından doldurulur.


public class SimpleMovieFinder : IMovieFinder, IServicable

{


private IMovieDatabase database;


public SimpleMovieFinder()

{

}


public void Service(ServiceManager manager)

{


database = (IMovieDatabase)manager.Lookup(“MovieDatabase”);

}


public string[] GetByTheatre(long theatreId)

{


string filter = string.Format(“[TheatreId] = {0}”, theatreId);


return database.GetByFilter(filter);

}

}

Dependency Injection kullanılan sınıfın yapısını bozmaz. Bu, modülleri
herhangi bir container nesnesi olmadan da kullanabileceğimiz anlamına geliyor.
İyi dizayn edilmiş modülleri IoC kuralına göre kullanmak için her hangi bir
değişiklik yapmamıza da gerek yoktur. Üçüncü parti modülleri bile IoC
uygulamaları içinde kullanabiliriz.

Diğer taraftan yukarıda anlatılan “Contextualized Dependency Lookup” metodu
sınıfın yapısını bozar. Tipik olarak bu, modüllerin bir container içinde
kullanılmasını ve uygun bir framework’ten ithal edilmesini gerektirir. Ayrıca
IoC yaklaşımında kullanılacaksa ekstra iş gerektirir. Hal böyle olunca CDL çok
az kullanım alanı bulmaktadır.

Konfigürasyon

IoC framework, “dependency resolution” prensipleri ile aynı prensipleri
kullanarak konfigürasyon servisleride sunar. Örneğin SmtpEmailSender isimli bir
sınıfın aşağıdaki gibi bir constructor sunduğunu düşünelim. IoC framework
otomatik olarak bu konfigürasyon detaylarını doldurur ve bizim bu iş için ayrıca
kod yazmamıza gerek kalmaz. Bunu bir XML konfigürasyon dosyası kullanarak yapar.

public SmtpEmailSender(string
serverHostname, int serverPort)

Eğer hazır sınıfları IoC framework içine dahil ediyorsak “Setter Injection”
metodunu kullanarak konfigürasyon parametrelerini doldurabiliriz.

Yaşam Süreci

Bir modülün yaşam süreci, oluşturulmasından sonra hangi aşamalardan
geçeceğini tayin etmektir. Diyelim ki modülümüz bir kaç “thread” açıyor ve bir
“socket” üzerinden dinleme yapıyor. Bu genelde constructor seviyesinden olacak
bir iş. IoC framework için kullanılacak modüllerin bir start/stop arayüzü vardır
ve “container” bu arayüz ile modüllerin yaşam sürecini kontrol edebilir.

Daha karmaşık yaşam süreçleri için bazı IoC framework’lerinde daha detaylı
yönetim biçimleri vardır.

Neden DIP Kullanalım?

Tabii ki iki tane sınıf için IoC sisteminin uygulanması zaman harcamaktan
başka bir şey olmaz. IoC sisteminin yararı karmaşık bağımlılık bulunduran
uygulamalarda
ortaya çıkar. Yandaki şemaya bir göz atalım. Bu şemada yeşiller sınıfları
sarılarda arayüzleri temsil eder. Sınıflardan her hangi birini kullanmaya
kalktığınızda ortaya çıkacak kod karmaşasını tahmin edebiliyor musunuz?

Uygulama terimi içinde, DIP çok basit bir teknik. Tek başına tüm nesneleri
yaratıp parametreleri oluşturan bir uygulamadan ziyade, DIP ile bir dizi gevşek
bağlı modül olarak geliştirme yapmak çok daha karlı. Ayrıca bu modüllerin
birbirleri ile nasıl ilişki kuracaklarını bilmelerine gerek yok. Çüknü tüm
modüller bir container nesnesi içinde çalışıyorlar ve container nesnesi
kofigürasyon dosyasından (XML formatında bir dosya) gerekli tüm parametreleri ve
birlikte çalışma prensiplerini alıyor.


http://blogs.pragprog.com/cgi-bin/pragdave.cgi/Tech/TransparentIOC.rdoc

Mevcut IoC Sistemleri

IoC kavramı henüz .NET dünyasında tam olarak gerçeklenebilmiş değil. Bunun
sonucu olarak ortaya çıkan IoC framework’leri Java dünyasında olduğu kadar
zengin değil. Aşağıda iki IoC sisteminin kısa açıklamalarını bulacaksınız.

Spring.NET (http://www.springframework.net/)

Java Spring Framework’ünden adapte edilmiştir. (http://www.springframework.com/)

Bazen başka bir sistemden adapte etmek iyi bir yöntem olmuyor ve sonuçta aynı
fonksiyonellik sunulabilmesi için pek çok takla atmak gerekiyor. Bu da sistemin
performansını etkiliyor

Java içindeki IoC kavramı daha iyi oturduğu için Spring.NET aynısını yapmaya
çalışıyor. Kavram olarak iyi sayılabilir.

Kullanılan diğer modüllerin otomatik olarak bağlanması yok.

XML konfigürasyon dosyaları çok karmaşık ve bazen kodunuda yazmak gerekiyor.

Konfigürasyon dosyalarının kod ile olan senkronizasyonu kolayca
bozulabiliyor.


Castle Framework (http://www.castleframework.net/)

Kullanılan diğer modüllerin otomatik olarak bağlanması var.

Anlaşılabilir konfigürasyon dosyaları.


<component id=”smtpEmailSender”>


<parameters>


<serverHostname>mail.ssw.com.au</serverHostname>


<serverPort>25</serverPort>


</parameters>


</component>

Dökümantasyon yok denecek kadar az ve varolan dökümantasyon değişiklik
olduğunda güncellenmiyor.

IoC’nin uygulanması

Container Oluşturmak

IoC tüm nesnelerimizi yönetmek için bir “container” nesnesini
kullanıyor. Container nesnesinin sorumluluğu kullanılan diğer nesnelerin
istendiği zaman hazır edilmesidir.
Container nesnesinin işini yapabilmesi
için kullanılacak nesnelerin container nesnesine eklenmesi ve sundukları
servislerin tanımlanması gerekir. Nesnelere daha sonra erişebilmek ve
konfigürasyon dosyaları ile bağlamak içinde tekil bir anahtar verilir.

Container nesnesini yaratmak kolay:

IWindsorContainer container = new
WindsorContainer();

Ve kullanılacak nesneleri de eklemek çok zor değil. Ayrıca nesneye verdiğimiz
tekil anahtara dikkat edin. Daha sonra konfigürasyon dosyalarına bağlamak için
işimize yarayacak. Birde arayüz referansına dikkat edin IBulkEmailSender ile
nesnenin sunduğu servislere erişim sağlanıyor.


container.AddComponent(“newsletterBulkEmailSender”,


typeof(IBulkEmailSender),


typeof(NewsletterBulkEmailSender));

İhtiyacımız olduğunda kullanacağımız fonksiyonun tipine bağlı olarak herhangi
bir nesneyi çağırabiliriz. Tabii ki bunu arayüzü kullanarak yapacağız. Bir
satırda arayüzün ismini 3 kere kullanıyoruz ama genede tüm sınıfları birbirine
kodla bağlamaktan daha az kod yazıyoruz.


IBulkEmailSender sender = (IBulkEmailSender)container[typeof(IBulkEmailSender)];

Posted in Bilişim, English.