SOLID 1 – Tek Sorumluluk Prensibi

Evet arkadaşlar, uzunca bir süredir, modern C++ ile ilgili yazılar paylaşıyordum sizler ile ve yazılım mühendisliğine ilişkin konulara biraz uzak kaldık. Bu yazı ile birlikte, biraz daha bu kavramlara eğilmeye başlayacağım. Daha önce bazı kavramlara ilişkin yazılarım olmuştu, inşallah önümüzdeki dönemde bunların sıklığını arttırmayı planlıyorum. Gelelim bu yazımın (daha doğrusu artık yazı dizisi) konusuna.
Yazımı tek bir kelime ile özetleyecek olursam: SOLID. Pek anlamlı gelmemiş olabilir. S.O.L.I.D. şeklinde yazarsam bazı kelimelerin kısaltmaları olduğunu hemen anlayacaksınız.

Sizleri daha fazla merakta bırakmadan, S.O.L.I.D.’in neyin kısaltması olduğunu sizler ile paylaşayım.

S – Single-responsiblity principle (SRP)
O – Open-closed principle (OCP)
L – Liskov substitution principle (LSP)
I – Interface segregation principle (ISP)
D – Dependency Inversion Principle (DIP)

Bunların her birisi, zamanında Robert C. Martin, kendisi Bob amca olarak da bilinir, tarafından Nesne Yönelimli Yazılım Geliştirme için uyulması gereken asgari prensipler olarak tanımlanmıştır. Daha fazla detay için şu dokümana da göz atabilirsiniz. Nesne yönelimli programlama yaklaşımı, yapısal programlama yaklaşımına göre tekrar kullanılabilirliği/esnekliği arttırıp, karmaşıklığı kotarabilse de, yanlış kullanım, bunun tam tersine de sebep olabilir. Bu prensiplerin temel amacı da aslında bu tarz sıkıntıları gidererek, daha çevik, tekrar kullanılabilir ve idame edilebilir yazılımlar elde etmek.

Bu bağlamda, nesne yönelimli yazılım geliştiren hatta, çoğu yazılımcının bu kavramlara vakıf olması, bana kalırsa oldukça elzem. O sebeple daha fazla vakit kaybetmeden bunların her birisine göz atalım.

Bütün bu prensipleri tek bir yazıda anlatmaktansa, ayrı ayrı yazılarda anlatmaya karar verdim. Hem daha anlaşılır hem de daha kolay okunabilir olacağını düşünüyorum. Bu yazımda Tek Sorumluluk “Single-Responsibility” prensibine göz atacağız.

Diğer SOLID yazılarına aşağıdaki adreslerden ulaşabilirsiniz.

  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

Tek Sorumluluk Prensibi (Single-Responsiblity Principle):

Image result for single responsibility image

Prensibin isminden de anlaşılacağı gibi, bu kavramın en önemli amacı: ilgili kodun, nesne yönelimli yazılımlar için, sınıfın, sadece tek bir sorumluluğu olmasıdır. Bu biraz da aslında, yazılım tasarımı için önemli olan tutarlılık/kohezyon (“cohesion“) kriteri ile de yakından ilintilidir. Bu kavram, verilen bileşenin metot ve verilerinin birbirleri ve gerçekleştirilecek olan yegane fonksiyonalite ile ne kadar ilintili olduğunu ifade eder. Yüksek düzeyde kohezyon, iyi bir yazılım tasarımı elde etmede büyük katkı sağlar.

Bob amca, bu prensibe dair ayrıca “sınıfı değiştirmemiz için sadece bir sebebimiz olmalı” gibi oldukça ünlü bir tanım da yapmıştır (bunun detayları için verdiğim şu adrese bir göz atabilirsiniz). Bir diğer ifade ile sınıf içerisindeki elemanların değişimi aynı sebepten olmalı ve değişiklik için farklı sebeplere ihtiyaç duyulan şeyler ayrılmalıdır. Peki sebepten kasıt nedir? Burada kısaca bir örnek üzerinden bunu anlatmaya çalışayım.

Örneğin elimizde, uygulamamızın dış dünya ile haberleşmesinden sorumlu bir sınıf olduğunu düşünelim ve çok temel olarak aşağıdaki kabiliyetleri içerdiğini farz edelim:

Yukarıda verdiğim sınıfa baktığımızda, temel olarak iki fonksiyon görülmekte: TCP/IP soketi üzerinden haberleşme ve haberleşme geçmişinin kaydedilmesi. Mesaj gönderilmesi ve mesaj geçmişin kaydedilmesinin her birini ayrı bir kabiliyet olarak düşünebiliriz. Bu bağlamda olabilecek değişiklikleri öngördüğümüzde, burada kayıt için XML formatı kullanılırken, JSON’ı destekleme durumu olabilir. Aynı zamanda TCP yerine seri kanal ya da UDP kullanma durumu olabilir. Gördüğünüz üzere, bu sınıf değişmesi için birden fazla sebep ortaya çıktı! SRP’ye göre bunlardan sadece birinin değişikliğe sebebiyet vermesini bekliyoruz. Şimdi bu kod parçasını SRP’ye uygun hale getirmeye çalışalım.

Öncelikli olarak mesaj geçmişini yazan işlevi ayrı bir sınıfa alalım. Bunu da yaparken birden fazla kayıt formatı olabileceğini düşünelim. Bu durumu kotarmak için aşağıdaki gibi sınıfları oluşturabiliriz:

Artık yeni bir mesaj kayıt formatı kullanmamız gerektiğinde, ExternalCommunication sınıfını değiştirmemize gerek kalmayacak. Benzer şekilde haberleşme sınıfı için de aşağıdaki sınıfları tanımlayıp kullanabiliriz.

Nihai olarak sınıfımız aşağıdaki gibi olacak ve SRP’i da sağlamış ve oldukça genişleyebilir bir sınıf elde etmiş olduk 🙂

Yukarıdaki örnek yanında, Bob amcanın kullandığı örneği de sizler ile paylaşmak istiyorum. Aşağıda, örneğe ilişkin sınıf diyagramını görebilirsiniz.

Violation of SRP

Burada görüldüğü üzere Rectangle sınıfımızın en az iki adet sorumluluğu var. Bunlar: GraphicalApplication tarafından, grafiksel kullanıcı arayüzü (GUI) aracılığı ile bir dikdörtgen çizdirilmesi  (draw() API’si) ve ComputationalGeometryApp tarafından dikdörtgenin alanının hesaplanılarak kullanılması (area() API’si). İlk bakışta herhangi bir problem görülmeyebilir, hadi biraz daha yakından bakalım.

Buradaki göreceğimiz ilk sıkıntı, sadece dikdörtgen alan hesaplamasına ihtiyaç duyan ComputationalGeometryApp sınıfının, dolaylı yoldan GUI’ye de bağımlılık içermesidir. Benzer şekilde GUI ile ilgili bir değişiklikten ötürü ComputationalGeometryApp uygulamasına da bu değişiklik yansıyabilir. Bu da aslında değişim için sadece bir sebep olmalı kuralını ihlal ediyor.

Peki bu sıkıntıları nasıl giderebiliriz? Hemen bunu gideren tasarıma göz atalım:

Design SRP-proof

Görüleceği üzere bağımlılıkların yukarıda gösterildiği şekilde ayarlanması ile iki uygulama arasındaki bağımlılık da ortadan kalkmış oldu ve tasarımımız da SRP ile uyumlu hale geldi.

Yukarıdaki örneklerden sonra akla şu tarz bir soru gelebilir? Peki gerçekten ilgili sınıfımızın birden fazla sorumluluğu olup olmadığını nasıl anlayabiliriz? Bu noktada imdadımıza, yazımın başlarında bahsettiğim kohezyon kavramı yetişiyor. Buna örnek teşkil etmesi açısından SRP is a Hoax sayfasında verilen Java örneğine göz atacağız:

Şimdi bu sınıf özelinde SRP’yi körü körüne uygulamaya kalktığımızda, muhtemelen elimizde ExistanceChecker, ContentReader ve ContentWriter gibi sınıflar olacaktır. Basit bir şekilde Aws sınıf üzerinde bir veri okuyup, görüntülemek için aşağıdaki gibi bir kodu kullanmamız gerekecektir:

Görüleceği üzere, çoğu zaman bu sınıflar birlikte kullanılacaklar. Ayrıca bu sınıflar, birbirleri ile yüksek kenetlenme (coupling) ve düşük seviyede kohezyon sergiliyorlar. O sebeple, bu sınıfı aslında bu şekilde ayırmak doğru değil.

Normal şartlarda da bu prensibi kullanırken, sınıflarınızı özellikle kohezyon seviyelerini iyi değerlendirmeniz gerekiyor.

Bu soru ile birlikte ilk prensibimizi tamamlamış oluyoruz. Haftaya ikinci prensipte görüşmek üzere, kendinize iyi bakın.

Kaynaklar:

Bir cevap yazın

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

Bu site, istenmeyenleri azaltmak için Akismet kullanıyor. Yorum verilerinizin nasıl işlendiği hakkında daha fazla bilgi edinin.