Derinlemesine yazılım eğitimleri için kanalımı takip edebilirsiniz...

Arayüz Ayrım Prensibi(Interface Segregation Principle – ISP)

Arayüz Ayrım Prensibi(Interface Segregation Principle - ISP)

Merhaba,

Önceki yazılarımda SOLID prensiplerinden STek Sorumluluk Prensibi(Single Responsibility Principle – SRP)‘ni, OAçık Kapalı Prensibi(Open Closed Principle – OCP) ve L-Liskov’un Yerine Geçme Prensibi(Liskov Substitution Principle – LSP) incelemiştik. Bu içeriğimizde ise sıradaki I harfine denk gelen Arayüz Ayrım Prensibini(Interface Segregation Principle – ISP) ele alacağız.

Biliyoruz ki, Interface’ler sınıfların anlaşmalarıdır. Haliyle bir sınıfın implement ettiği bir Interface zorunlu şekilde ilgili sınıfa uygulanmaktadır. Hal böyleyken söz konusu Interface yetersiz ya da fazla geliyorsa, o sınıfı dağıtmak yerine yeni bir Interface oluşturmak daha makbuldür. İşte, ISP’nin de ortaya koyduğu mantık budur.

Bu mantıkla yola çıkarak ISP’nin, hemen hemen her farklı yetenek için farklı bir arayüz tanımlamasını önerdiğini söyleyebiliriz.

Bir yandan soyutlama maksatlı kullanacağımız Interface yapısı içerisinde her türlü metodu ve işlemi barındırmamız, o Interface’i implement eden bazı sınıflarda uygulanan elemanların kimileri Dummy Code(Sahte Kod) dediğimiz bir vaziyette niteliksiz yer işgal edeceklerdir. Yani ISP, bir sınıfa hiç gereği yokken filanca metodları ve işlevleri implemente etmenin önüne geçecek bir prensiptir.

Tabi ki de çoğu konuda olduğu gibi bu konuda da örnek bir senaryo üzerinden mevzuyu ele alacağız. Haliyle benim örneklendirmem ordu üzerine bir senaryodan ibaret olacaktır. Düşünelim ki, elimizde birden fazla tankımız olsun. Ve bu tankların özelliklerini barındıracak bir Interface’imiz olsun.

Arayüz Ayrım Prensibi(Interface Segregation Principle - ISP)
Evet, gördüğünüz gibi tek bir Interface ile elimizdeki tankların özelliklerini şablonize ettik. Lakin, “Tank2” isimli tankımızın ateş etme özelliğinin olmadığını ve “Tank3” isimli tankımızın ise düşman ile mesafe ölçme yeteneği olmadığını varsayarsak bundan dolayı “Tank2” için “AtesEt” ve “Tank3” için “DusmanIleMesafeOlc” isimli metodlar işlevsiz kalacaktır.

Alın size Dummy Code(Sahte Kod) durumu.

İşte böyle bir durumda ilgili metodları farklı Interface’lere alarak daha doğrusu ‘arayüzleri ayırarak’ bu soruna çözüm bulabiliriz.

Tabi öncelikle yukarıdaki Class Diyagramının kodlarını aşağıda paylaşarak durumu içerikten daha net anlamanızı sağlayalım.

    class Program
    {
        static void Main(string[] args)
        {
        }
    }
    interface ITank
    {
        bool AtesEt();
        bool HareketEt();
        double DusmanIleMesafeOlc();
    }
    class Tank1 : ITank
    {
        public bool AtesEt() => true;
        public double DusmanIleMesafeOlc()
        {
            //Mesafa ölçme işlemleri
            return 100;
        }
        public bool HareketEt() => true;
    }
    class Tank2 : ITank
    {
        public bool AtesEt()
        {
            //Tank2'nin ateş etme özelliği yoktur!
            return false;
        }
        public double DusmanIleMesafeOlc()
        {
            //Mesafa ölçme işlemleri
            return 230;
        }
        public bool HareketEt() => true;
    }
    class Tank3 : ITank
    {
        public bool AtesEt() => true;
        public double DusmanIleMesafeOlc()
        {
            //Tank3'ün mesafa ölçme özelliği yoktur!
            return -1;
        }
        public bool HareketEt() => true;
    }

Şimdi tasarımımızı ISP’ye dikkat ederek yeniden şekillendirelim.
Arayüz Ayrım Prensibi(Interface Segregation Principle - ISP)
Aslında bu Class Diyagram herşeyi özetlemektedir. Genede biz ne olup bittiğini açıkça metine dökelim.

Birbirlerinden bağımsız birden fazla yeteneği tek bir Interface’de tutmak yerine, her yetenek farklı bir Interface yapısında tutulmakta ve ihtiyac doğrultusunda ilgili sınıfa ilgili arayüzler uygulanmaktadır. Bu sayede lüzumsuz metod ve işlev uygulaması yapılmamakta ve ilgili Derived Class’larımızın nitelikleri neyse içerikleride birebir aynı olmaktadır.

İşte Arayüz Ayrım olarak nitelendirilen bu faydalı ve oldukça kullanışlı manevra boşuna prensip edinilmemiştir.

Tasarımımızın son durumunu kod olarak aşağıda paylaşalım.

    interface ITankAtesEt
    {
        bool AtesEt();
    }
    interface ITankMesafeOlc
    {
        double DusmanIleMesafeOlc();
    }
    interface ITankHareketEt
    {
        bool HareketEt();
    }
    interface ITank : ITankMesafeOlc, ITankHareketEt, ITankAtesEt
    {
    }
    class Tank1 : ITank
    {
        public bool AtesEt() => true;
        public double DusmanIleMesafeOlc()
        {
            //Mesafa ölçme işlemleri
            return 100;
        }
        public bool HareketEt() => true;
    }
    class Tank2 : ITankHareketEt, ITankMesafeOlc
    {
        public double DusmanIleMesafeOlc()
        {
            //Mesafa ölçme işlemleri
            return 230;
        }
        public bool HareketEt() => true;
    }
    class Tank3 : ITankAtesEt, ITankHareketEt
    {
        public bool AtesEt() => true;
        public bool HareketEt() => true;
    }

Okuduğunuz için teşekkür ederim…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar dilerim…

Bunlar da hoşunuza gidebilir...

9 Cevaplar

  1. engin dedi ki:

    çok güzel bir yazı olmuş. ben birşey sormak istiyorum. özetle her sınıfın bir arayüzü olmalı diyebilir miyiz?

    • Gençay dedi ki:

      ISP çerçevesinde böyle bir özete varmamız pek mümkün değildir lakin her bir classa anlaşma olarak bir interface tanımlamak ileride bağımlılıkları yönetmek açısından faydalı olabilir. Tabi her classtan kastım, sürekli gelişecek ve kullanışı bol olacak sınıflardır. Artı olarak aynı kategoride farklı işlev yapan sınıflarda bağımlılıkları soyutlamak için arayüzleri yahut abstract sınıfları kullanmak ileriye dönük oldukça karlı bir yatırım olmaktadır.

      Teşekkür ederim.
      Sevgilerimle…

  2. uuunal dedi ki:

    Uçak – Tank – Kuş örnekleri arasında ben pek bir fark göremedim. Kısacası LSP ile ISP bana aynı işlevi görüyor gibi geldi. Acaba farkları nelerdir?

    • Gençay dedi ki:

      LSP’de bir nesne birden fazla arayüz tarafından imzalanmakta, bundan dolayı bu arayüzler bu nesneyi refere edebilmektedirler. ISP’de ise arayüzler ayrılmakta ve her bir sınıf ihtiyaca dönük arayüz uygulamaktadır.

      • uuunal dedi ki:

        Yani Liskov’un Yerine Geçme Prensibini sağlayan bir koda Arayüz Ayrım Prensibini sağlar diyebilir miyiz o halde?

        Yazılarınız çok güzel olmuş bu arada teşekkürler.

        • Gençay dedi ki:

          Evet, aslında zahiren diyebiliriz.

          Nihayetinde ISP’de esas birbirinden farklı aksiyonları farklı arayüzlerde tanımlamakken, LSP’de ise lüzumsuz yapıları kodumuzdan arındırabilmek için aksiyonları farklı arayüzlere ayırmaktır. Haliyle bir LSP doğal olarak ISP’yi kapsamaktadır. Dikkat ederseniz; LSP, OCP’yi de kapsamaktadır. Biryandan tüm prensipler pratikte SRP’yi önceliklemeden kendilerini anlamlandıramamaktadırlar. Anlayacağınız prensipler birbirlerinden bağımsız değildir. Prensipler bir yapı yahut obje olmadıklarından dolayı, adı üzerinde projede sürecinde inşada edinmemiz gereken düsturlarsa eğer tabi ki de doğrusu birbirlerini kapsamaları, desteklemeleri ve beraber bir bütün olmalarıdır. Burada dikkat edilmesi gereken, mantıksal olarak prensipleri birbirlerinden ayırmaktır.

          Teşekkür ederim…
          Sevgilerimle…

  1. 12 Aralık 2020

    […] özellikleri karşılayabilmekte mümkündür lakin bu sefer de arayüzleri şişirmenin sonucunda arayüz ayrım prensibine aykırı hareket edilmiş olacaktır. Hem aynı arayüzden türeyen kimi nesneler için yeni […]

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

*