SOLID Prensipleri

Dependency Inversion Principle

D — Bağımlılığın Tersine Çevrilmesi Prensibi

Amaç: Üst seviye modüller alt seviye modüllere bağımlı olmamalıdır; ikisi de soyutlamalara bağımlı olmalıdır. Soyutlamalar detaylara değil, detaylar soyutlamalara bağımlı olmalıdır.

1

Basit Anlatım

Teknik terim yok — günlük hayattan bir benzetme

Evinizdeki lambayı düşünün. Lamba duvara doğrudan lehimlenmiş olsaydı ne olurdu? Lambayı değiştirmek istediğinizde duvarı kırmanız, elektrik tesisatını sökmeniz gerekirdi. Başka bir odaya taşımak imkânsız olurdu.

Bunun yerine lamba bir fişe, duvar da bir prize sahiptir. Lamba duvarı tanımaz, duvar da lambayı tanımaz. İkisi de sadece "priz standardını" bilir. Böylece lambayı istediğiniz an değiştirebilir, duvara da lamba yerine şarj aleti takabilirsiniz.

Patron ve çalışan

Bir şirket müdürü "Bu raporu Ahmet hazırlasın" derse, Ahmet izne çıktığında iş durur. Bunun yerine "Bu raporu bir analist hazırlasın" derse, analist yetkinliğine sahip herkes işi yapabilir. Müdür bir kişiye değil, bir role bağımlıdır.

Bağımlılığın Tersine Çevrilmesi şunu der: "Somut kişilere ya da parçalara değil, rollere ve standartlara bağlan. Böylece parçalar değişse de sistem ayakta kalır."
2

Teknik Anlamı

Bir yazılımcının bakış açısıyla

Geleneksel katmanlı mimaride üst seviye iş mantığı (ör. OrderService), alt seviye detaylara (ör. SqlRepository, SmtpClient) doğrudan bağımlıdır. Bu durumda veritabanını veya e-posta sağlayıcısını değiştirmek iş mantığını da değiştirmeyi gerektirir.

DIP bu bağımlılığı tersine çevirir: İş mantığı kendi ihtiyaç duyduğu arayüzü (IOrderRepository) tanımlar; alt seviye sınıflar bu arayüzü uygular. Artık ok yönü detaydan soyutlamaya doğrudur.

DIP, DI ve IoC farkı

İhlal belirtileri

Faydaları

3

C# ile Uygulama

Adım adım, çalışan kod örneği

Senaryo: Bir sipariş servisi siparişi kaydediyor ve müşteriye bildirim gönderiyor.

Adım 1 YANLIŞ Üst seviye sınıf, somut detaylara kaynaklanmış

OrderService kendi bağımlılıklarını new ile oluşturuyor. SMS'e geçmek veya test için sahte veritabanı kullanmak imkânsız; OrderService'i değiştirmek zorundayız.

public class SqlServerDatabase
{
    public void Insert(string order) => Console.WriteLine($"  [SQL Server] {order} eklendi");
}

public class SmtpEmailClient
{
    public void Send(string to, string msg) => Console.WriteLine($"  [SMTP] {to}: {msg}");
}

public class OrderService
{
    private readonly SqlServerDatabase _db = new();      // somut sınıfa bağımlılık
    private readonly SmtpEmailClient _email = new();     // somut sınıfa bağımlılık

    public void PlaceOrder(string customer, string product)
    {
        _db.Insert(product);
        _email.Send(customer, $"{product} siparişiniz alındı.");
    }
}

Adım 2 DOĞRU Soyutlamalar: iş mantığının ihtiyaç tanımı

Arayüzler, onları kullanan üst seviye modülün ihtiyacına göre tasarlanır; veritabanının veya e-posta kütüphanesinin yeteneklerine göre değil.

public interface IOrderRepository
{
    void Save(string customer, string product);
}

public interface INotifier
{
    void Notify(string customer, string message);
}

Adım 3 DOĞRU Üst seviye modül: sadece soyutlamaları bilir

Bağımlılıklar constructor üzerinden enjekte ediliyor (Dependency Injection).

public class OrderService
{
    private readonly IOrderRepository _repository;
    private readonly INotifier _notifier;

    public OrderService(IOrderRepository repository, INotifier notifier)
    {
        _repository = repository;
        _notifier = notifier;
    }

    public void PlaceOrder(string customer, string product)
    {
        _repository.Save(customer, product);
        _notifier.Notify(customer, $"{product} siparişiniz alındı.");
    }
}

Adım 4 DOĞRU Alt seviye detaylar: soyutlamaları uygular

Yeni bir altyapı eklemek = yeni bir sınıf yazmak. OrderService değişmez.

public class SqlOrderRepository : IOrderRepository
{
    public void Save(string c, string p) => Console.WriteLine($"  [SQL Server] {p} kaydedildi");
}

public class PostgresOrderRepository : IOrderRepository
{
    public void Save(string c, string p) => Console.WriteLine($"  [PostgreSQL] {p} kaydedildi");
}

public class EmailNotifier : INotifier
{
    public void Notify(string c, string m) => Console.WriteLine($"  [E-posta] {c}: {m}");
}

public class SmsNotifier : INotifier
{
    public void Notify(string c, string m) => Console.WriteLine($"  [SMS] {c}: {m}");
}

Adım 5 DOĞRU Test için sahte (fake) uygulamalar

Veritabanı veya ağ olmadan iş mantığını test edebiliyoruz.

public class InMemoryOrderRepository : IOrderRepository
{
    public List<string> Saved { get; } = new();
    public void Save(string c, string p) => Saved.Add(p);
}

public class FakeNotifier : INotifier
{
    public int Count { get; private set; }
    public void Notify(string c, string m) => Count++;
}

Adım 6 Kullanım

Bağımlılıklar tek bir yerde (uygulamanın başlangıcında, "composition root") bir araya getiriliyor. ASP.NET Core'da bu işi DI container yapar.

Console.WriteLine("Kötü tasarım (değiştirilemez):");
new Kotu.OrderService().PlaceOrder("ali@mail.com", "Laptop");

Console.WriteLine("\nİyi tasarım - SQL Server + E-posta:");
new OrderService(new SqlOrderRepository(), new EmailNotifier())
    .PlaceOrder("ali@mail.com", "Laptop");

Console.WriteLine("İyi tasarım - PostgreSQL + SMS (OrderService değişmedi):");
new OrderService(new PostgresOrderRepository(), new SmsNotifier())
    .PlaceOrder("0555 111 22 33", "Telefon");

Console.WriteLine("\nBirim testi (veritabanı ve ağ olmadan):");
var repo = new InMemoryOrderRepository();
var notifier = new FakeNotifier();
new OrderService(repo, notifier).PlaceOrder("test", "Kalem");
bool passed = repo.Saved.Contains("Kalem") && notifier.Count == 1;
Console.WriteLine($"  Test sonucu: {(passed ? "BAŞARILI" : "BAŞARISIZ")}");

// ASP.NET Core'da aynı bağlama işini DI container yapar:
//   builder.Services.AddScoped<IOrderRepository, SqlOrderRepository>();
//   builder.Services.AddScoped<INotifier, EmailNotifier>();
//   builder.Services.AddScoped<OrderService>();

Konsol çıktısı

Kötü tasarım (değiştirilemez):
  [SQL Server] Laptop eklendi
  [SMTP] ali@mail.com: Laptop siparişiniz alındı.

İyi tasarım - SQL Server + E-posta:
  [SQL Server] Laptop kaydedildi
  [E-posta] ali@mail.com: Laptop siparişiniz alındı.
İyi tasarım - PostgreSQL + SMS (OrderService değişmedi):
  [PostgreSQL] Telefon kaydedildi
  [SMS] 0555 111 22 33: Telefon siparişiniz alındı.

Birim testi (veritabanı ve ağ olmadan):
  Test sonucu: BAŞARILI

Kodda dikkat edilmesi gerekenler

Özetle: İş mantığınızı somut altyapı sınıflarına değil, kendi tanımladığı soyutlamalara bağlayın; detayları dışarıdan enjekte edin.
← Ana sayfaya dön