Yazılım Mimarileri ve Tasarım Desenleri Üzerine

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.


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.

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…

Exit mobile version