Bu tasarım prensibi, uygulamalardaki alt ve üst sınıflar arasındaki bağımlılıklardan dolayı ortaya çıkmıştır. Alt sınıfları, ana işleri yapan sınıflar olarak düşünelim. Üst sınıflar da alt sınıflara bağlı olarak işlemler yapan sınıflar olarak düşünelim. Yani üst sınıflar bazı işlemleri gerçekleştirebilmek için alt sınıflara muhtaçtır. Doğal olarak üst sınıflar, alt sınıflardan oluşturulmuş nesneleri kullanarak süreci işleteceklerdir. Ancak üst sınıfların direk alt sınıf nesnelerine bağlı olması bir takım zorlukları beraberinde getirmektedir. Bu zorlukların başında sıkı bağ oluşumu gelmektedir. Sıkı bağlılık ise gelişimi ve test edilebilirliği olumsuz etkileyen bir durumdur.
Yazılım tasarımında sıkı bağlılığı ortadan kaldıran çözüm ise nesneleri direk kullanmak yerinde soyut nesneler yani arayüzler (interface) kullanmaktır. Yani üst sınıflar, nesnelerin kendisini tanımak yerine soyut nesneleri tanırlar.
Bir örnek üzerinden kötü ve iyi kod tasarımlarını inceleyelim.
Sıkı Bağlı Tasarım
public class DisplayServiceManager { readonly AnalogDisplayService service = new AnalogDisplayService(); public DisplayServiceManager(AnalogDisplayService service) { this.service = service; } public void StartService() { service.Start(); } public void StopService() { service.Stop(); } } public class AnalogDisplayService { public void Start() { // Start analog display. } public void Stop() { // Stop analog display. } }
Bu tasarımda örnek olarak görüntü yönetimini yapan DisplayServiceManager adında, üst sınıf olan bir servis yöneticisi oluşturulmuştur. Bu üst sınıf AnalogDisplayService adında bir alt sınıfı kullanmaktadır. Yukarıda belirttiğimiz gibi, üst sınıf alt sınıf nesnesini somut olarak kullanmıştır. Yani üst sınıf, servisleri tanımaktadır. Bu da alt sınıflara sıkı bağlılık oluşturmaktadır. Bu durumda DigitalDisplayService adında yeni bir ihtiyaca karşılık, yönetici üst sınıfın yapısı değişecek.
Yani şu anki durumumuz şu şekildedir:
Üst Sınıf + Alt Sınıf
Oysa istenen durum şu şekildedir:
Üst Sınıf + Soyut Nesne + Alt Sınıf
Bağımlılığa yeni bir boyut kazandıran tasarımı ise şu şeklide yapabiliriz.
Dependency Inversion Yöntemiyle Tasarımı
public class DisplayServiceManager { private IDisplaySerivce service; public DisplayServiceManager(IDisplaySerivce service) { this.service = service; } public void StartService() { service.Start(); } public void StopService() { service.Stop(); } } public interface IDisplaySerivce { void Start(); void Stop(); } public class AnalogDisplayService : IDisplaySerivce { public void Start() { // Start analog display. } public void Stop() { // Stop analog display. } }
Bu kod, Dependency Inversion prensibinin uygulandığı bir koddur. Sıkı bağlılığın IDisplayService arayüzü sayesinde ortadan kalktığını görmüş olduk. Yeni eklemeler için DisplayServiceManager sınıfında değişikliğe gerek kalmadı. Esnek bir yapıya sahip olduk.