SOLID Prensipleri

Interface Segregation Principle

I — Arayüz Ayrımı Prensibi

Amaç: İstemciler kullanmadıkları metotlara bağımlı olmaya zorlanmamalıdır. Büyük, genel arayüzler yerine küçük ve amaca özel arayüzler tercih edilmelidir.

1

Basit Anlatım

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

Bir restorana gittiniz ve sadece bir kahve içmek istiyorsunuz. Garson size 40 sayfalık bir menü getiriyor: kahvaltılar, çorbalar, ana yemekler, tatlılar, şarap listesi… Kahveyi bulmak için tüm menüyü karıştırıyorsunuz. Bunun yerine ayrı bir içecek menüsü olsaydı çok daha kolay olurdu.

İş ilanı örneği

Bir şirket "garson" arıyor ama ilanda şunlar yazıyor: "Yemek pişirebilmeli, muhasebe bilmeli, uçak kullanabilmeli, elektrik tesisatı döşeyebilmeli." Hiçbir garson bunların hepsini yapamaz. Yapamadığı işler için de "bunu yapamam" demek zorunda kalır.

Her işe bir uzmanlık tanımı olsaydı (garson, aşçı, muhasebeci), herkes sadece kendi yapabildiği işin tanımına uyardı.

Arayüz Ayrımı Prensibi şunu der: "Kimseyi, ihtiyacı olmayan şeyleri kabul etmeye zorlama. Büyük bir sözleşme yerine küçük ve net sözleşmeler yap."
2

Teknik Anlamı

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

ISP, "şişman" (fat) arayüzlerin parçalanmasını önerir. Bir sınıf bir arayüzü uyguladığında, arayüzdeki tüm metotları uygulamak zorundadır. Arayüz, sınıfın gerçekten yapamadığı işleri içeriyorsa sınıf bu metotları boş bırakır ya da istisna fırlatır.

Neden sorun?

İhlal belirtileri

.NET'ten örnekler

SRP sınıflara, ISP arayüzlere odaklanır. İkisinin de özünde aynı fikir vardır: yüksek uyum (high cohesion).
3

C# ile Uygulama

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

Senaryo: Bir ofiste farklı yeteneklere sahip cihazlar var: basit yazıcı, tarayıcı ve çok fonksiyonlu cihaz.

Adım 1 YANLIŞ Her şeyi içeren şişman arayüz

Basit yazıcı tarama ve faks yapamadığı halde bu metotları uygulamak zorunda. İstemci IMachine gördüğünde hangi metodun çalışacağını bilemez.

public interface IMachine
{
    void Print(string doc);
    void Scan(string doc);
    void Fax(string doc, string number);
}

public class BasicPrinter : IMachine
{
    public void Print(string doc) => Console.WriteLine($"  Yazdırıldı: {doc}");

    public void Scan(string doc) =>
        throw new NotSupportedException("Bu yazıcı tarama yapamaz!");

    public void Fax(string doc, string number) =>
        throw new NotSupportedException("Bu yazıcı faks gönderemez!");
}

Adım 2 DOĞRU Küçük, amaca özel arayüzler

Her arayüz tek bir yeteneği temsil ediyor. Sınıflar sadece gerçekten yapabildiklerini uyguluyor.

public interface IPrinter { void Print(string doc); }
public interface IScanner { string Scan(string doc); }
public interface IFax     { void Fax(string doc, string number); }

public class BasicPrinter : IPrinter
{
    public void Print(string doc) => Console.WriteLine($"  [Yazıcı] {doc} yazdırıldı");
}

public class DocumentScanner : IScanner
{
    public string Scan(string doc)
    {
        Console.WriteLine($"  [Tarayıcı] {doc} tarandı");
        return doc + ".pdf";
    }
}

public class MultiFunctionDevice : IPrinter, IScanner, IFax
{
    public void Print(string doc) => Console.WriteLine($"  [ÇokFonk] {doc} yazdırıldı");
    public string Scan(string doc)
    {
        Console.WriteLine($"  [ÇokFonk] {doc} tarandı");
        return doc + ".pdf";
    }
    public void Fax(string doc, string number) =>
        Console.WriteLine($"  [ÇokFonk] {doc} -> {number} numarasına fakslandı");
}

Adım 3 DOĞRU İstemciler: sadece ihtiyaç duydukları yeteneği isterler

ReportService yalnızca yazdırmaya ihtiyaç duyuyor; IPrinter istemesi yeterli. Böylece hem basit yazıcıyla hem çok fonksiyonlu cihazla çalışır.

public class ReportService
{
    private readonly IPrinter _printer;
    public ReportService(IPrinter printer) => _printer = printer;

    public void PrintMonthlyReport() => _printer.Print("Aylık Rapor");
}

public class ArchiveService
{
    private readonly IScanner _scanner;
    public ArchiveService(IScanner scanner) => _scanner = scanner;

    public void Archive(string doc) =>
        Console.WriteLine($"  Arşive eklendi: {_scanner.Scan(doc)}");
}

Adım 4 Kullanım

Tüm parçaları bir araya getiren istemci (client) kodu:

Console.WriteLine("Şişman arayüz:");
Kotu.IMachine machine = new Kotu.BasicPrinter();
machine.Print("Sözleşme");
try { machine.Scan("Sözleşme"); }
catch (NotSupportedException ex) { Console.WriteLine($"  ÇÖKTÜ: {ex.Message}"); }

Console.WriteLine("\nAyrılmış arayüzler:");
var basic = new BasicPrinter();
var mfd = new MultiFunctionDevice();

new ReportService(basic).PrintMonthlyReport();
new ReportService(mfd).PrintMonthlyReport();
new ArchiveService(new DocumentScanner()).Archive("Fatura");
new ArchiveService(mfd).Archive("Dekont");
mfd.Fax("Teklif", "0212 555 00 00");

// new ArchiveService(basic);  // DERLEME HATASI: BasicPrinter bir IScanner değil

Konsol çıktısı

Şişman arayüz:
  Yazdırıldı: Sözleşme
  ÇÖKTÜ: Bu yazıcı tarama yapamaz!

Ayrılmış arayüzler:
  [Yazıcı] Aylık Rapor yazdırıldı
  [ÇokFonk] Aylık Rapor yazdırıldı
  [Tarayıcı] Fatura tarandı
  Arşive eklendi: Fatura.pdf
  [ÇokFonk] Dekont tarandı
  Arşive eklendi: Dekont.pdf
  [ÇokFonk] Teklif -> 0212 555 00 00 numarasına fakslandı

Kodda dikkat edilmesi gerekenler

Özetle: Büyük ve genel arayüzler yerine küçük, amaca özel arayüzler tasarlayın; hiçbir sınıf kullanmadığı veya yapamadığı metotlara bağımlı olmasın.
← Ana sayfaya dön