Dependency Inversion Principle (DIP)

Dependency Inversion Principle (DIP)

“High-level modules should not depend on low-level modules. Both should depend on abstractions.”

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

Prensip temel olarak, üst modüller (sınıflar) ve alt modüller (sınıflar) arasında kuvvetli bir bağ olmamasını gerektiği ve alt ve üst modüller (sınıflar) arasında sadece soyut (abstract) bir bağ olmasını önermektedir.

Örnek bir senaryo üzerinde bu prensibi açıklamayalım. İngilizce’den Fransızca’ya metin çevirisi yapan bir uygulamamız olduğunu düşünelim ve üst ile alt sınıfların kuvvetli bir bağ ile birbirine bağlı olduğunu aşağıda gözlemleyelim.

namespace TestApp
 {
   class Program
   {
     static void Main(string[] args)
     {
       TextTranslator translator = new TextTranslator();
       //Metini çevir
       translator.Translate();
     }
   }
   //Translator ingilizce okuyup fransızca çevirim işlemi yapacaktır.
   public class TextTranslator
   {
     public void Translate()
     {
       English english = new English();
       French french = new French();
       //İngilizce oku
       string englishText = english.Read();
       //Fransızca'ya çevirip yaz
       french.Write(englishText);
     }
   }
   public class English
   {
     public string Read()
     {
       return "How are you?";
     }
   }
   public class French
   {
     public void Write(string input)
     {
       Console.WriteLine("French: Comment êtes-vous?");
     }
   }
 }

Görünüşte ilk bakıldığında çok basit gibi duran bu program zaman içerisinde gereksinimlere bağlı olarak değişebilir, örneğin İngilizce’den Hollandaca’ya çeviri yapma ihtiyacımız olabilir. Bu durumda TextTranslator sınıfında aşağıdaki gibi bir geliştirme ile ihtiyacımızı karşılayabiliriz.

//Translator bu sefer metni İngilizce okuyup Fransızca veya Hollandaca yazacaktır. 
public class TextTranslator
{
    public void Translate(string translateTo)
    {
        English english = new English();
        //İngilizce metni oku
        string englishText = english.Read();

        if (translateTo == "French")
        {
            French french = new French();
            //Metni Fransızca'ya çevirip yaz
            french.Write(englishText);
        }
        else if (translateTo == "Dutch")
        {
            Dutch dutch = new Dutch();
            //Metni Hollandaca'ya çevirip yaz
            dutch.Write(englishText);
        }
    }
}

Ancak TextTranslator sınıfı normalde giriş dili ve çevrilecek dile bağımlı olmamalıdır. Fakat bizim durumumuzda Translator metodu İngilizce’den Fransızca’ya yada İngilizce’den Hollandaca’ya çeviri yapacağını bilmektedir. Translator metodunun işi dil detaylarını(İngilizce,Fransızca, Hollandaca vs. sınıflarını) bilmeden çeviri işlemlerini yapabilmektir. Öyleyse burada prensip tanımında belirttiğimiz gibi bir geliştirme davranışı sergilemeyip bu prensibi ihlal etmiş bulunmaktayız.

“Abstraction should not depend on details, instead details should depend on abstraction”

Peki bu durumda soyut üst sınıfımız (modülümüz) nerede?

Prensiptede anlatılmak istenildiği gibi soyutlama sınıf bazında olmak zorunda değildir, bizim durumumuzda da operasyon bazında soyutlama yapmamız gerekir. Bu yüzden operasyonlarımızı (çeviri işlemlerini) yapan Translate metodu İngilizce, Hollandaca yada Fransızcaya kuvvetli bağlar ile bağlı olmamalıdır.

Peki belirli bir alt dile (İngilizce, Hollandaca, Fransızca sınıflarına) bağlı olmadan kodu tekrar yazarsak :

namespace TestApp
 {
   class Program
   {
     static void Main(string[] args)
     {
       TextTranslator translator = new TextTranslator();
       //Metni çevir
       translator.Translate(new English(),new French());
       translator.Translate(new English(),new Dutch());
     }
   }
   public class TextTranslator
   {
     // Yazılan metni giriş yapılan dilden istenilen dile çevirip yazalım
     public void Translate(IInputLanguage inputLanguage, IOutputLanguage outputLanguage)
     {
       //Metni Oku 
       string text = inputLanguage.Read();
       //Metni Yaz
       outputLanguage.Write(text);
     }
   }
   //Soyut (Abstract) giriş dillerinin implement edeceği interface
   public interface IInputLanguage
   {
     string Read();
   }
   //Çevirisi yapılacak alt(Concrete) sınıf
   public class English : IInputLanguage
   {
     public string Read()
     {
       return "How are you?";
     }
   }
   //Soyut (Abstract) çeviri yapacak çıkış dillerinin implement edeceği interface
   public interface IOutputLanguage
   {
     void Write(string input);
   }
   //Çeviri yapacak alt(Concrete) sınıf
   public class French : IOutputLanguage
   {
     public void Write(string input)
     {
       Console.WriteLine("French: Comment êtes-vous?");
     }
   }
   //Çeviri yapacak alt(Concrete) sınıf
   public class Dutch : IOutputLanguage
   {
     public void Write(string input)
     {
       Console.WriteLine("Dutch: Hoe gaat het?");
     }
   }
 }

Dikkat edileceği üzere Translate metodu artık İngilizce,Fransızca yada Holladaca dillerine bağlı değildir. Soyut olan IInputLanguage ve IOutputLanguage interface’lerine bağlıdır. Çevirilecek ve çeviri yapılacak diller tamamen soyutlanmıştır. Prensip ihlalide böylece giderilmiştir. Böylece daha esnek ve birbirine sıkı bağlı olmayan genişleyebilir bir tasarım ortaya çıkmıştır. İleride karşılaşılacak yeni ihtiyaçlarıda daha basit bir biçimde gerçekleştirebilir hale gelinmiştir.

Örneğin Hollandacadan İngilizceye çeviri yapılmak istenildiğinde

  • Dutch sınıfının IInputLanguage interface’ini implement edip gerekli metodunu yazması
  • English sınıfının IOutputLanguage interface’ini implement edip gerekli metodunu yazması yeterli olacaktır.
public class English : IInputLanguage,IOutputLanguage
{
   public string Read()
   {
      return “How are you?”;
   }
   public void Write(string input)
   {
      Console.WriteLine(“How are you?”);
   }
}
public class Dutch : IInputLanguage,IOutputLanguage
{
   public string Read()
   {
      return (“Dutch: Hoe gaat het?”);
   }
   public void Write(string input)
   {
      Console.WriteLine(“Dutch: Hoe gaat het?”);
   }
}

Resim, © Jennifer M. Kohnke
Agile Principles, Patterns, and Practices in C# 
Xeon2k Blog

0 thoughts on “Dependency Inversion Principle (DIP)”

Bir cevap yazın