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.
Tekrardan merhabalar,
Hocam yazınız için teşekkür ederim, bir sorum olacak. Dependency inversion principle ile open/closed principle arasındaki fark nedir? Ben iki arasında bir fark göremedim maalesef 🙁
İyi çalışmalar.
@caner, Dependency inversion yöntemi ile bir ana sınıfın ihtiyaç duyduğu nesneleri ana sınıfın içinde oluşturmak yerine, sınıf dışında başka bir yerde oluşturduktan sonra ana sınıfın içerisine almaktır. Yani ana sınıfın bağımlılığını tersine çevirmektir. Open Closed ilkesi tanım olarak bir sınıfın değişime değil gelişime açık olmasıdır. Bir sınıfım gelişime açık olması için uygulanan yöntemlerden biri de dependency inversion yöntemidir. Bu sebepten bu iki kavram iç içe geçmiştir. Ama biri diğerinin aynısıdır diyemeyiz. Çünkü bir sınıfın gelişime açık olmasının başka yöntemleri de olabilir.
Sanırım open/closed ilkesinde verilen örneklerin genelinde dependency inversion kullanılması, farklı bir örneğe denk gelmemiş olmak karıştırdı. Yani open closed ilkesi uygulanan bir yöntem çok, uygulanan bir fikir ve yöntemler dizisidir diyebilir miyiz?
Evet örnekler birbirinin aynısı da olabilir, bu yüzden karışıklık yaşamanız normal.