SOLID 4 – Arayüz Ayrıştırma Prensibi

Evet dostlar, SOLID serimize devam ediyoruz. SOLID serisi ile ilgili diğer yazılar ve aynı zamanda SOLID prensipler genel anlamda neye hizmet ediyor ve genel motivasyon için de ilk yazıya aşağıdaki bağlantılardan başvurabilirsiniz:

  1. SOLID 1 – Tek Sorumluluk Prensibi
  2. SOLID 2 – Açık/Kapalı Prensibi
  3. SOLID 3 – “Liskov Substitution” Prensibi
  4. SOLID 4 – Arayüz Ayrıştırma Prensibi

Arayüz Ayrıştırma  Prensibi

Related image

Artık son iki prensibe geldik. Bunlardan ilki arayüz ayrıştırma (“interface segragation”) prensibi. Aslında ismi size bir fikir vermiştir diye düşünüyorum ama biz yine de bu prensibe ilişkin Bob C. Martin tarafından yapılan tanıma bir bakalım:

The interface-segregation principle (ISP) states that no client should be forced to depend on methods it does not use.

Direk çevirecek olursak, ISP, herhangi bir istemci, kullanmadığı bir metoda bağımlı olmamalıdır. Bir diğer ifade ile birden fazla, iyi bir şekilde tanımlanmış arayüz, tek ve genel bir arayüzden daha iyidir. Özellikle Açık/Kapalı Prensibinde ifade ettiğimiz üzere, arayüzler aslında iki nesne arasında, bir nevi kontrat/aracı görevi görmektedir. Bu sayede, kullanıcı sınıf, servisi gerçekleyen sınıfın bunu nasıl gerçeklediğine bağımlı olmamaktadır. Sunucu, istemciye, ilgili arayüzde tanımlanan davranışının sunulacağını garanti eder.

Şimdi gelelim bu prensibin çıkış noktasına. Diyelim bir sınıf tasarladınız ve bu sınıf birden fazla istemci (ya da başla sınıflar) tarafından kullanılmakta. Bu istemcilerin her birinin de farklı metotlar ve servisler kullandığını düşünelim, burada tek bir arayüz tanımladığınız durumda, arayüzde sunulan metotlar kullanılmasa da, hepsini tanımlamak zorunda kalırsınız. Bütün kullanıcılar da bu arayüze bağımlı kalırlar ve kullanılmayan bu arayüzlere ilişkin gerçekleştirilecek her türlü değişikliklerden, bu istemciler de etkilenecektir. Aslında temelde, ilgili arayüzleri kullanan istemciler, bütün sunulan arayüzlere erişmeye de ihtiyaçları yok. Bunun yerine, Bob amcanın da ifade ettiği üzere, daha küçük ve sadece ilgili istemcilerin ihtiyaç duyacağı metotları içeren, rol arayüzler tanımlanmalıdır.

Tabi burada aklımız hemen, kardeşim her bir istemci için ayrı ayrı arayüz mü tanımlayayım, bu sefer de arayüz çöplüğüne dönmesin? Kesinlikle haklısınız, burada önemli olan, istemcileri ve ihtiyaç duydukları belirleyerek konsolide etmek ve benzer istemciler, ilgili arayüzler sunmaktır. Tahmin edebileceğiniz üzere, burada sınıflara ilişkin sunulacak olan arayüzleri doğru belirlemek oldukça önemli. Lakin, aşağıda verdiğim bir çok kaynakta da ifade edildiği üzere bunu yapabilmek için biraz alan bilgisine de ihtiyaç da var.  Bunu yapmak için, olası alandaki kullanım senaryoları incelenebilir, olası etkileşimler ve arayüzler belirlenebilir. Arayüzler belirlendikten sonra da muhakkak kendimize “Bu arayüzdeki bütün metotlara ihtiyacım var mı, kullanıyor muyum? Eğer değilse, bunları nasıl daha küçük arayüzlere ayırabilirim?” sorularını sormalıyız.

Evet sözü daha fazla uzatmadan hemen bir örnek üzerinden (Örneğin kaynağı da yine Robert C. Martin’in yazısı) bu prensibi biraz daha derinlemesine inceleyelim. Aşağıdaki figürde, üç kullanıcısı olan bir sınıf gösterilmekte. Mevcut sınıf, bu üç istemciye de servis sunuyor, yani ilgili metotlar tanımlanmış. Service sınıfında gerçekleştirilecek küçük bir değişiklik durumunda her üç istemcinin de bir daha derlenerek, kullanıma sunulması gerekmekte.

Peki bu sınıfı nasıl daha iyi hale getirebiliriz? Her bir istemci için gerekli arayüzleri tanımlar ve istemcileri sadece bunlara bağımlı hale getirebiliriz. Şöyle ki:

Görüleceği üzere, artık her bir istemci sadece ilgilendiği arayüzü kullanmakta ve Service sınıfı da bütün bu arayüzleri gerçeklemekte. Diyelim Service A da bir değişiklik oldu bu durumda sadece Client A‘nın güncellenmesi (derlenerek, kullanıma sunulması) yeterli olacaktır ve diğerleri bu değişiklikten etkilenmeyecekler. Peki, farklı arayüzlerin ortak metotları kullandığı durumda ne yapacağız? Bunlar için ayrı bir ortak arayüz mü tanımlayacağız? Bu durumda, bu metotları her bir arayüz için çoklamakta bir sıkıntı yok, zaten Service sınıfı bunu gerçekleyecek ve aynı servis her bir arayüz aracılığı ile sunulacaktır. Eğer, arayüzlere ilişkin çok ciddi güncellemelerin yapılması veya eklemelerin yapılması durumunda, mevcut arayüzleri değiştirmektense, yeni arayüzlerin tanımlanması tercih edilebilir.

Sonuç olarak burada tanımlayacağımız arayüzlerin olabildiğince küçük, kohezif ve sağlam olmasına dikkat etmeliyiz. Eğer bir arayüzde yapılan değişiklik, bunun ile hiç ilgisi olmayan başka bir arayüzü etkiliyorsa, bir yerlerde yanlış yaptığımız anlamına gelebilir. Bu prensibe uyulmaması sonucu, sınıflar arasında gereksiz, bağımlılıklara sebebiyet vermiş olacağız ve bu da uzun vadede bizlere bakım ve idame maliyeti olarak geri dönecektir.

Bu arada, literatürde bu şekilde bir çok servisin tek bir arayüzden sunulması “fat interfaces” ya da “interface pollution” gibi ibarelerle de ifade edilmekte. Bir diğer ifade ile, çok fazla davranış/metodun tek bir arayüz aracılığı ile sunulması. İsimleri de zaten pek çekici değil, öyle değil mi? Bu prensibin, bu anlamda detaylı bir şekilde incelenmesini ve örneklendirilmesini şu yazıda görebilirsiniz.

Bunun ile birlikte işin kodlama kısmına baktığınızda, bir çok dil bu anlamda mekanizmalar sunuyor. Örneğin, C++ farklı soyut arayüz sınıflarından sınıflarınızı türetmenize, çoklu miras mekanizması ile, olanak sağlıyor. Tabi bu çoklu miras konusunda her zaman dikkatli olmakta fayda var. Benzer şekilde C# ve Java gibi programlama dillerinde de, sınıflar birden fazla arayüzden türetilebilmektedir.

Son olarak, bu prensip ile SRP, yani tek sorumluluk prensibi aslında çatışmıyor mu diye aklınıza bir soru gelebilir. Sonuçta, SRP’de gördüğümüz üzere her bir sınıfın tek bir sorumluluğu olması gerektiğini ifade etmiştik, burada ise tek bir arayüz yerine birden fazla arayüz kullanmaktan bahsettik. Bu birden fazla arayüz, birden fazla sorumluluk anlamına gelmiyor mu? Aslında gelmiyor, SRP yazımızdan bir örnek vermeye çalışayım. Orada örneğin hem haberleşme sorumluluğu hem de raporlama sorumluluğu tek bir sınıfa yüklenmişti ve biz de bunları ayırmıştık. Ama daha da ileri gidip, haberleşme kabiliyetini, veri gönderme ve alma olarak ayırmadık. Çünkü bu iki alt kabiliyet aslında birbirleri ile ilintili ve bunları ayırmak pratik değil. Bunu yerine bunları aynı sınıfın iki farklı arayüzü ile sunabiliriz. Aşağıda bu doğrultuda güncellenmiş kodu görebilirsiniz. Bu anlamda hem SRP hem de ISP’yi sağlamış oluyoruz.

SRP olaya, biraz daha sınıfın tasarımı noktasından yaklaşırken, ISP daha çok, istemci gözünden bakıyor. Bu iki prensibe ilişkin diğer karşılaştırma sonuçlarına kaynaklar kısmından ulaşabilirsiniz.

Sonuç olarak, bu prensip bize, büyük ve genel bir arayüzdense, istemcilere özel birden fazla, küçük ve öz arayüzler sunmamızı önermekte. Ayrıca, soyutlamalarımıza doğru bir şekilde yapıp/yapmadığımıza dair de bir fikir vermekte ve mevcut yazılımların tasarımları için de, bu anlamda önemli bir göstergedir. Bu şekilde geliştirmiş olduğumuz yazılımlar sayesinde, gerçekleşebilecek (ki her yazılım için bu kaçınılmazdır 🙂 değişiklikler, hızlı ve daha kolay bir şekilde uygulayabileceksiniz.

Bir sonraki ve son SOLID prensibinde görüşmek dileğiyle…

Kaynaklar:

Bir cevap yazın

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.