KategorilerNesne Yönelimli ProgramlamaProgramlama PrensipleriYazılım Mimarisi

Dependency Inversion Prensibi Örnekli Anlatım

SOLID prensilerinin “D”si, Dependency Inversion Prensibi (DIP) yani Bağımlılıkları Tersine Çevirme Prensibi SOLID prensiplerinin sonuncusudur. Dependency Inversion Prensibi yüksek seviye modüllerin düşük seviye modüllere bağımlı olmaması gerektiğini, bunun yerine ikisinin de soyutlamalara(abstractions) bağlı olması gerektiğini söylüyor.

Bu prensip loose coupling(gevşek bağlılık) denen prensibe dayanıyor, bunu da yüksek seviye ve düşük seviye sınıflar arasında soyut bir katman oluşturarak yapıyor.

Peki DIP ne işe yarar?

Bu prensip sayesinde esnek, bakımı kolay ve daha kolay test edilebilir bir koda sahip oluyorsunuz.

Bir örnek vererek devam edelim:

Mesaj göndermek için tasarladığımız bir sistem olduğunu düşünelim, Mesaj göndermek için bir MessageSender sınıfımız olsun, bu sınıftan da E-posta gönderme özelliğini içinde barındıran EmailService sınıfımız aracılığıyla Email gönderelim. Bizim burada üst seviye sınıfımız-modülümüz MessageSender sınıfı oluyor alt seviye sınıfımız-modülümüz de EmailService sınıfımız oluyor. Çünkü MessageSender Sınıfımız E-posta, SMS gönderme gibi işlemleri yaptığımız ana sınıf.

Dependency Inversion Prensibine göre MessageSender sınıfımız EmailService sınıfımıza bağımlı olmamalı.

İlk olarak nasıl olmaması gerektiğine bir göz atalım.

// Düşük seviye modül: Eposta gönderme fonksiyonunu sağlar
public class EmailService
{
    public void SendEmail(string recipient, string message)
    {
        Console.WriteLine($"{recipient} e-posta adresine mesaj gönderiliyor, mesaj: {message}");
    }
}


// Yüksek seviye modül: email servisini kullanarak e-posta gönderir
public class MessageSender
{
    private EmailService emailService;
    public MessageSender()
    {
        this.emailService = new EmailService();
    }

    public void SendEmailMessage(string recipient, string message)
    {
        this.emailService.SendEmail(recipient, message);
    }
}

Bu örneğimizde yüksek seviye olan MessageSender sınıfımız düşük seviye olan EmailService sınıfımıza doğrudan bağımlı oldu. Bu durum Dependency Inversion Prensibini ihlal ediyor.

Peki ne şekilde yaparsak bu doğrudan bağımlılıktan kurtulmuş oluruz?

// Mesaj göndermek için yapılan soyutlama
public interface IMessageSender
{
    void Send(string recipient, string message);
}

// Düşük seviye modül: Eposta gönderme fonksiyonunu sağlar
public class EmailService : IMessageSender
{
    public void Send(string recipient, string message)
    {
        Console.WriteLine($"{recipient} e-posta adresine mesaj gönderiliyor, mesaj: {message}");
    }
}

// Yüksek seviye modül: email servisini kullanarak e-posta gönderir
public class MessageSender
{
    private IMessageSender sender;
    public MessageSender(IMessageSender sender)
    {
        this.sender = sender;
    }

    public void Send(string recipient, string message)
    {
        this.sender.Send(recipient, message);
    }
}

Bu düzenlenmiş kodda MessageSender sınıfı mesaj gönderme özelliğinin soyutlaması olan IMessageSender Interface’ine bağımlı. Bu yeni kod tasarımı mesajlaşma sisteminin geliştirilmesini kolaylaştırırken MessageSender sınıfını değiştirmeden yeni mesaj gönderme özellikleri eklenebilmesine imkan veriyor. Örneğin bir de SMS gönderme özelliği eklemek istedik:

// Düşük seviye modül: Sms gönderme fonksiyonunu sağlar
public class SmsService : IMessageSender
{
    public void Send(string recipient, string message)
    {
        Console.WriteLine($"{recipient} numarasına sms gönderiliyor, mesaj: {message}");
    }
}

IMessageSender Interface’inden türettiğimiz SmsService sınıfımızı koda eklediğimizde MessageSender sınıfında herhangi bir değişiklik yapmamız gerekmedi, doğrudan eklememiz bu sınıfı kullanılabilir duruma getirdi.

Peki Sms mi yoksa E-posta mı göndermek istediğimize nasıl karar vereceğiz, bu kod nasıl çalışacak?

class Program
{
	static void Main()
	{
		// MessageSender sınıfından nesne oluşturuyoruz ve sms mi yoksa eposta mı göndereceğimizi söylüyoruz
		MessageSender emailSender = new MessageSender(new EmailService());
		MessageSender smsSender = new MessageSender(new SmsService());

		// E-posta ve Sms gönderme işlemi
		emailSender.Send("ornek@example.com", "Bu bir e-posta mesajı.");
		smsSender.Send("0505*******", "Bu bir sms mesajı.");

	}
}

MessageSender sınıfından nesne türetirken bu sınıfa EmailService türünde bir nesne yollarsak E-posta göndermemizi sağlayacak metoda erişiriz. Eğer SmsService türünde bir nesne yollarsak da Sms göndermemizi sağlayacak metoda erişiriz. Böylece Dependency Inversion Prensibinin gerektirdiği üst seviye sınıfın alt seviye sınıfa doğrudan bağımlılığını kesmiş oluruz.

Önceki yazılar:

Solid Prensipleri
Single Responsibility Prensibi
Open-Closed Prensibi
Liskov Substitution Prensibi
Interface Segregation Prensibi

Bir Cevap Yazın