Single Responsibilities Principle — SRP

Tuba Saday
What is that  ‘SOLID’?
5 min readOct 6, 2021

Single Responsibilty, Robert Martin tarafından tanıtılmış olan bir yazılım tasarım prensibidir. İsminden de anlaşılacağı gibi bir araya gelmiş olan parçaların aynı amaca hizmet etmesi beklenir. Aynı amaca hizmet etmeyen parçalar bölünerek ayrıştırılmalı ve birlikte kullanılmak isteniyor ise başka bir modülde birleştirilmelidir.

SRP’nin kökeni:

Bu kavramın temelini oluşturan terim olan Cohesion ilk olarak Tom DeMarco ve Meilir Page-Jones tarafından tanıtılmıştır. Daha sonra Tom DeMarco ve Meilir Page-Jones ‘cohesion’ olarak adlandırdıkları bu terim Robert Martin tarafından devşirilerek Single Responsibility’e çevrilmiş ve bu şekilde popülerleşmiştir.

SRP’ye geçmeden önce bize yararlı olacak birkaç terime bakmamızda fayda var, bunlar Cohesion, Coupling, Tight Coupling ve Loose Coupling’dir.

Cohesion: Cohesion’ın kelime anlamı uyumdur. SRP’de ise Cohesion bir parça (class, modül vs.) içerisindeki elementlerin birbiriyle uyumunu belirtir.

cohesion ve coupling

Yukarıda gözüktüğü gibi her farklı renkli yuvarlağı, altıgeni ve üçgeni ayırmak yerine birbirleriyle uyumlu oldukları için her bir şekli aynı parçanın içerisine koyduk ve dış modülün hepsine ihtiyacı olduğunu varsayarak tüm şekil modüllerimizi ayrı bir modülde birleştirdik.

Coupling: Coupling’in kelime anlamı ise bağlantıdır. Fark ettiyseniz yukarıdaki görselde ayrı ayrı ayırdığımız yuvarlak, altıgen ve üçgeni başka bir modülde birleştirdik dış modülümüze şekillerimizi içeren modüllerimizi bağladık, yani aslında coupling yapmış olduk.

Bu iki terimin yanında farkında olmamızın avantaj sağlayacağı iki terim daha var, tight coupling ve loose coupling.

Tight Coupling : İki varlık arasındaki bağlantının sıkı olma durumudur. Yeni bir element eklenecek/silinecek iken bağımlı olan ana elementimizin de güncellenmesi gerekecektir. Bu tip bir bağlılık kodu hantallaştıracak, geliştirme eforunu arttıracak ve kodun yeniden kullanımını imkansız kılacaktır.

Yukarıdaki kod bloğunda gözüktüğü gibi yeni bir gezegen tipi eklenmesi gerektiğinde Universe sınıfımıza gidip bir case daha eklememiz gerekecek.

Ardalis’in çok sevdiğim bir sözü var ‘New is glue.’. Classlarımızın içerisinde yeni bir elementin örnek aldığımızda (bknz: SRP — Tight Coupling — Universe.cs @ 11,15,19. satırlar) içerisinde örnek alınan sınıfımızı (Universe.cs) örnek aldığımız alt sınıflara (Earth.cs,Saturn.cs,Mercury.cs) sıkı bir şekilde bağlamış oluruz.

new-is-glue by ardalis

Loose Coupling: İki varlık arasındaki bağlantının gevşek ve yönetilebilir olması durumudur. Yeni bir element eklenecek/silinecek iken bu alt elemenleri kapsayan elementimizde bir değişiklik yapılmaz. Bu tip bağımlılık kodu hafifletecek, geliştirme eforunu yüksek oranda düşürecek ve kodun yeniden kullanılabilirlik oranını arttıracaktır.

Bir sürü havalı terim öğrendiğimize göre şimdi ana konumuza geçebiliriz sanırım..

Asıl konumuz olan SRP’ye Robet Martin’in o çok meşhur sözü ile başlamak istiyorum.

“Bir sınıfın değiştirilmek için tek bir sebebi olmalıdır.”

Yani; her bir modülünüzün sadece bir sorumluluğu olmalıdır, eğer sadece bir sorumluluğu var ise o modül sadece o sorumluluk bazında değiştirilebilir.

Peki sorumluluk kavramını nasıl açıklayabilriz ?

Sınıfımızın sorumluluğu o sınıfımızın olası değiştirilme nedenleri ile doğru orantılıdır. Biz bu sınıfımızı kaç farklı sebep ile değiştirme ihtiyacı duyuyorsak veya duyabilir isek sınıfımız o kadar çok sorumluluğa sahiptir. Yani sınıfımız SRP’yi katlediyordur.

Peki bu tek sorumluluk ilkesi her sınıfın sadece bir metod içermesi mi demek ?

Kafalarımızın karıştığı tam da bu noktada devreye SRP’nin omurgası yani DeMarco ve Page-Jones’un ‘uyumluluk’ (Cohesion) kavramı giriyor. Cohesion ilkesine göre sınıfımız içerisindeki elementler uyumlu olduğu sürece büyüyebilir, uyumsuz olduğu yerlerde ise bu elemenlerin bu sınıftan uzaklaştırılması ve başka bir modül olarak ele alınması gerekir. BU da demektir ki; sınıfımızın tek bir sorumluluğu olmasına rağmen birden fazla methoda sahip olabilir.

Bunu en iyi şekilde aşçı örneği ile açıklayabiliriz belkide,

Bir aşçının görevleri sadece yemeği pişirmek ile sınırlı değildir, malzemeleri seçmek, malzemeleri hazırlamak, yapılacak yemeğin kişi sayısına göre malzemeyi ayarlamak, yemek piştikten sonra tabak dizaynını yapmak da bir aşçının görevidir. Görüldüğü gibi aşçının birden fazla görevi vardır ama hepsi birbiri ile uyumludur. Ama biz bu aşçıya bulaşık yıkama görevi atar isek SRP’yi katletmiş oluruz. Yani aşçıya kendi görev tanımı olmayan ve hali hazırdaki görevleri ile bir uyumu olmayan bir görev atamış oluruz bu da aşçının performansını düşürür ve hatta istifasına bile neden olabilir. Eğer biz de kodumuzda uyumu sağlamaz isek modüllerimizin geliştirme süresi artacak ve hata ile karşılaşma oranımız yükselecek.

Bir A modülünde yapılan değişiklik, B modülünü etkiliyorsa, bu demektir ki A modülünde uyumu bozan bir element var ve bu element B modülüne aktarılmalıdır.

Bu kadar konuştuk şimdi örneklerimiz ile pekiştirelim.

Öncelikle çok basit, her zaman karşılaşabileceğimiz bir kötü dizayn oluşturalım:

Görüldüğü gibi nesne örneklemelerimiz, loglamamız, email atma görevimiz vs bu sınıfta. Yani bu sınıfın değiştirilmek için birden çok sebebi var. Bu da demek tir ki sınıfımız SRP’yi katlediyor.

Peki bu durumu nasıl düzeltebiliriz,

Görüldüğü gibi bir soyutlama ile main sınıfımızdan loglama, mailing, çalışma ve tamamlanma güncellemelerimizi çıkardık. Main sınıfımız artık bu işlemlerin nasıl yapıldığını bilmiyor sadece kullanıyor. Bu sorumlulukları uyumluluk durumu düşünülerek KeeperTasks sınıfımıza yükledik.

Şuanki kodumuzda gördüğünüz gibi sıkı bağlanma durumu mevcut (bknz: 14,21,24. satır), bunu ise tight coupling kısmında öğrendiğimiz gibi bir interface yolu ile gevşek bağlamaya çevirebiliriz.

Böylece görevi tamamlama akışımızda veya iş yapma akışımızda bir değişiklik olur ise bunu Main sınıfımız bilmeyecek ve bu değişiklik main sınıfında herhangi bir değişikliğe neden olmayacak. Ve gözüktüğü gibi KeeperTask sınıfımızda birden fazla method var çünkü bu methodların birbiri ve sınıfı ile uyumu mevcut.

Diyelim ki ‘kendinitanıt’ adında bir metod yazmamız istendi, biz bunu KeeperTask sınıfımıza yazar isek sınıf içi uyumu bozmuş oluruz, sınıf içi uyumu düşündüğümüzde bu yeni metodumuzun uyumlu olduğu yer Keeper sınıfı olması gerektiği aşikardır. Eğer her ikisine de uyumlu olmayan bir metod yazmamız istenirse, bu yeni akış bazında yeni bir sorumluluk alanı olan bir sınıf yazmamız gerekecektir.

Özet:

Cohesion (uyum): “Tek bir modül içindeki işleme elemanlarının işlevsel olarak ilişkili olma derecesi.” [Structured Design -Edward Yourdon]

Coupling (bağlantı): “Modüller arasındaki karşılıklı bağımlılık derecesi” [Structured Design -Edward Yourdon]

Tight Coupling (sıkı bağlanma): “If you want to change the skin, you would also HAVE TO change the design of your body as well because the two are joined together — they are tightly coupled.” [BenKoshy] [1]

Loose Coupling (Gevşek bağlanma): “Gömleğinizi değiştirirseniz, vücudunuzu değiştirmek zorunda kalmazsınız — bunu yapabildiğiniz zaman, gevşek bir bağlantınız olur. Bunu yapamadığınız zaman, sıkı bir bağlantınız olur.” [BenKoshy] [1]

Single Responsibility: “Bir sınıfın değiştirilmek için tek bir sebebi olmalıdır.” [R. Martin aka Uncle Bob]

--

--

Tuba Saday
What is that  ‘SOLID’?

It’s a non stoppable learner and an insane developer.