Interface Segregation Principle (ISP)

“Clients should not be forced to depend on methods they do not use.”

Prensip temel olarak, genişletilecek sınıfların kullanmayacağı,
metodlar yada özellikleri içeren interface’leri yada ana soyut (base abstract) sınıfları;
birbiriyle olan ilişkileri (cohesive) ve işlevlerine göre ayrı interface’lere ayırmamız gerektiğini belirtir.

Peki böyle bir işlemin bize ne anlamda yararı olacaktır? Örneğin bir sınıf tasarladığımızda bir çok özelliği ve metodu olan bir interface implement ettiğimizi düşünelim. Interface’ i implement ettik ancak tasarladığımız sınıfın ihtiyacı olmayan bir çok özellikte sınıfımıza gelmiş oldu. Bu özelliklerin bazılarını implement ettiğimizi bazılarınıda etmediğimizi düşünelim. Tasarladığımız sınıfı kullanan diğer sınıflar implement etmediğimiz bir özellik yada metodu kullanmaya çalıştığında hata alacaklardır. Buda daha önce anlattığımız LSP ve SRP prensiplerinde bahsettiğimiz durumlara aykırı düşecek ve istenmeyen bir durum oluşturacaktır.


Örnekle açıklayacak olursak, aşağıdaki sınıfa baktığımızda yetki işlemlerimizi gerçekleştiren taban bir sınıf olduğunu gözlemleyebiliriz.

public enum RestrictedOperation
{
    DoThis,
    DoThat,
    DoTheOtherThing,
}
 
public class MyAuthorizer
{
    public bool IsAllowed(RestrictedOperation operation)
    {
        // yetki kontrolü için db den kontrolü yapan kodlar.
    }
 
    public void AssignToGroup(int userId, int groupId)
    {
        // kullanıcıyı bir gruba atayan kodlar.
    }
 
    public void RemoveFromGroup(int userId, int GroupId)
    {
        // kullanıcıyı bir gruptan silen kodlar.
    }
 
    void Allow(int groupId, RestrictedOperation operation)
    {
        // grup kullanıcılarına belirli bir operasyon için izin veren kodlar.
    }
 
    public void Prohibit(int groupId, RestrictedOperation operation)
    {
        // grup kullanıcılarına belirli bir operasyonu engelleyen izin veren kodlar.
    }
}

Bu sınıf ilk bakışta Single Responsibility Principle’ine uymaktadır ve sorun yokmuş gibi gelebilir. Ancak yukarıda bahsettiğimiz üzere genişletilecek sınıfın ihtiyacı olamayan özellikler ve metodlar işin içerisine girdiğinde böyle bir yapıyı interface’lere ayırmamız gerekir.

Peki böyle bir düzeltmeye bu durumda neden ihtiyaç olabilir? Bir web sitesinde bu yapıyı kullandığımızı düşünelim her kullanıcı aynı haklara mı sahip olacaktır? Nadirende olsa bazı kullanıcılar Admin haklarına sahip olup diğer kullanıcıları gruplara atamak ve yetki ayarı yapmak ister.

Böyle bir istek göz önüne alındığında bu yapıyı admin ve sıradan kullanıcı işlemlerine göre iki interface ayırarak tasarlayabiliriz:

// Sıradan kullanıcıların yetki sorgularını gerçekleştirebilmesi 
// için implemente edeceği Interface
public interface IAuthorizerUser
{
    void Allow(int groupId, RestrictedOperation operation);
}
 
// Admin'lerin istediği işlemleri gerçekleştirebilmesi 
// için implemente edeceği Interface
public interface IAuthorizerAdmin
{
    void Allow(int groupId, RestrictedOperation operation);
    void Prohibit(int groupId, RestrictedOperation operation);
    void AssignToGroup(int userId, int groupId);
    void RemoveFromGroup(int userId, int GroupId);
}

Bu iki interface’ i implement etmiş yeni MyAuthorizer sınıfımız:

public class MyAuthorizer : IAuthorizerUser, IAuthorizerAdmin
{
    // işlemler
}

Sonuç itibarı ile admin ve kullanıcı işlemlerini işlevsel olarak böldük, bu durumda nadirde olsa admin işlemleri gerçekleştirecek kullanıcı sınıfları için IAuthorizerAdmin interface’ini kullanabiliriz. Normal kullanıcı sınıfları içinde IAuthorizerUser interface’i kullanılabilir.

Interface kullanarak işlevselliğe göre ayırmanın önemli diğer bir yararı dependency injection yapacağımız zaman çok fazla efor sarfetmemiş olacağımızdır.

Kaynaklar;

Agile Principles, Patterns, and Practices in C# 
Larry Spencer’s Blog

0 thoughts on “Interface Segregation Principle (ISP)”

Bir cevap yazın