SOLID Prensipleri Nedir? Ne Değildir?

Burak Coşkun
Bilişim Hareketi
Published in
5 min readJun 6, 2019

SOLID yazılım prensipleri diye adlandırılan ve dünya üzerinde, OOP nesne tabanlı yazılım geliştirirken kullanılan standartlaştırılmış 5 önemli tasarım ilkesi vardır.

Bu 5 tasarım ilkesi, Robert C. Martin (Bob Amca) tarafından Design Principles and Design Patterns kitabında tanıtılmıştır.

Bu ilkelerin amacı, yazılım tasarımlarını daha anlaşılır, bakımı daha kolay ve genişletilmesi daha kolay hale getirmektir.
Bir yazılım mühendisi olarak, bu 5 prensibi bilmek çok önemlidir!

Bu makalede, bu ilkeleri ele alacağım, nasıl ihlal edildiğine dair örnekler vererek ve yazılımda doğru tasarımın yapılırken kullanılması, uyulması gereken prensiplerini incelemeye başlayalım.
Örnekler C # ile verilecektir ancak herhangi bir OOP dili için de geçerlidir.

  • S is single responsibility principle (SRP)
  • O stands for open closed principle (OCP)
  • L Liskov substitution principle (LSP)
  • I interface segregation principle (ISP)
  • D Dependency injection principle (DIP)

1. Single responsibility principle (SRP)

Bir class bir sorumluluk almalı yani bir class’ın isviçre çakısı gibi bir sürü görevi, özelliği olmamalıdır. Aşağıdaki ilk örnekteki gibi, hem nesnesinin özelliklerini barındırıp hem de bir kaç metodun birleşiminden oluşan bir metodumuz olmamalıdır. Bazen uğraşmak istemediğimiz için bazı class lar içine tüm metodları aynı sınıfın içine doldurabiliyoruz. Bu sebeple class larımız on binlerce satıra ulaşabiliyor. Hatta o kadar büyük oluyorki editör ile bu class açıldığında kasabiliyor bile..

Öyle bir tasarım yapmalıyız ki, her class veya metod tek bir sorumluluğu olmalıdır. Yani, class veya metodun değişmek için bir sebebi olmalıdır. Eğer birden fazla sebebi var ise birçok sorumluluğu var demektir ve bu şekilde yazılan kodların anlaşılması veya tekrar kullanılabilmesi neredeyse imkansızdır.

Aşağıdaki kod ta “Employee” sınıfı 2 sorumluluk alıyor, biri çalışan veritabanı işlemlerinin sorumluluğunu üstleniyor, diğeri ise çalışanların raporlarını oluşturuyor. Employee sınıfı, rapor oluşturma sorumluluğunu üstlenmemelidir, çünkü müşteriniz sizden raporu Excel’de veya başka bir dosya türünde raporlama isteme olasılığı olduğunu düşünürsek, bu class’ın değiştirilmesi gerekecektir.

Bu nedenle, SRP prensibine göre, bir class bir sorumluluk almalı, bu yüzden rapor oluşturma işlemi için farklı bir class yazmalıyız ve rapor oluşturma class’ındaki herhangi bir değişiklik “Employee” class’ını etkilememelidir.

2. Open Closed Principle (OCP)

Şimdi aynı “RaporOlustur” sınıfını bu prensipte örnek olarak alalım. Aşağıdaki sınıfta ile problemin ne olduğunu anlamaya çalışalım..

Evet haklısınız, çok fazla ‘if’ cümleleri varsa ve Excel ’gibi yeni bir rapor türü daha tanıtmak istiyorsak,‘ if’ yazmanız gerekir. Bu prensibe göre classlar gelişime açık, değişime kapalı olmalıdır. Ama bunu nasıl yapacağız?İnceleyelim..

Dolayısıyla, yeni bir rapor türü oluşturmak istiyorsak, sadece RaporOlustur class’ından miras alırız. Bu yüzden RaporOlustur gelişime açık fakat değişiklik için kapalıdır.

3. Liskov Substitution Principle (LSP)

Bir class ta bulunan özellikler, kendisinden kalıtım alan class’larda kullanılmayacaksa bu durum LSP’ye aykırı bir durumdur.

Yani kalıtım alınan class’ın içindeki özellikler kalıtımı alan class ta kullanılmalıdır. Yukarıdaki örnekte İlgili metod Guvercin için düzgün çalışmakta olup, tavuk için sorun olacaktır. Bu yüzden ilgili metodları aşağıdaki şekilde 2 ye bölmeliyiz.

İkinci senaryoda, Tavuk için Uc() metodu erişilemez olduğu için herhangi bir sorun ile karşılaşılmayacaktır.

4. Interface Segregation Principle (ISP)

Interface Segregation prensibine göre, “istemcilerin kullanmadıkları arayüzleri uygulamaya zorlanmaması gerektiğini” savunulmaktadır. Herbir interface’in belirli bir amacı olmalıdır. Tüm metodları kapsayan tek bir interface kullanmak yerine, herbiri ayrı metod gruplarına hizmet veren birkaç interface tercih edilmektedir.

Aşağıdaki futbolcu örneğinde farklı mevkilerde oynayan oyunculara nerdeyse tüm görevler verilmek zorunda kalınmıştır. Farklı mevkilerde oynayan futbolculara bulunan zorla olmaması gereken görevler yüklenmemelidir. Bu yüzden interface leri ayırmalıyız ve olması gereken kadar implemente etmeliyiz.

Yukarıdaki örnekte yapılan yanlışı şimdi düzeltelim. Aşağıdaki örnekte interface parçalara ayrılmıştır ve olması gerektiği kadar interface class’lara implemente edilmiştir.

Yukarıda da belirttiğim gibi yapı interface lere bölünerek gereksiz metodların kullanımı engellenmiştir.

5. Dependency Inversion Principle (DIP)

Robert C. Martin’in Dependency Inversion Prensibi’ne göre;

  • Üst seviye (High-Level) sınıflar alt seviye (Low-Level) sınıflara bağlı olmamalıdır, ilişki abstraction veya interface kullanarak sağlanmalıdır,
  • Abstraction(soyutlama) detaylara bağlı olmamalıdır, tam tersi detaylar abstraction(soyutlama)’lara bağlı olmalıdır.

Genel olarak geliştirilen uygulamalarda, üst seviyeli işlem yapan metodlar alt seviyeli işlem yapan metodlara bağımlıdır. Yani o metodları çağırırlar. Bu durum iyi değildir. Çünkü, alt seviye işlem yapan metodlar alt seviyede bulunan metodlar değişiklik gerektireceğinden dolayı, üst seviye metodlar da değişikliğe uğramak zorunda kalır.

Aynı şekilde üst seviyeli işlem yapan bir metotta yapacağımız bir değişiklik, bağlı olduğu tüm metodların değişmesini gerektirir. Birbirine bu kadar sıkı bağlı metodların bulunduğu bir projede tekrar kullanılabilirlik imkansız gibidir. Yani yapılan küçük bir değişikliği başka bir projede tekrar kullanmak istediğimizde birçok sınıfı projeye referans vermek zorunda kalırız. Bu aslında bir tekrar kullanabilirlik değildir çünkü herhangi bir soyutlama olmadığı için modülerlikten de söz edilemez. Ancak piyasaya baktığımızda birçok projede bu şekilde kodların olduğu görürüz ki bu nesne temelli tasarıma tamamen aykırıdır. Daha önceden de belirttiğimiz gibi nesne temelli bir dil kullanıyor olmak yazdığımız kodların da nesne temelli olacağını garanti etmez.

Nesne temelli bir yazılımda bu tarz bağımlılıklardan kurtulmak için soyutlama kullanılır. Bu şekilde üst seviye(high-level) class, alt seviye(low-level) class’lardan bağımlılığı ters çevrilerek alt seviye(low-level) class’ın üst seviye(high-level) class’a bağlı olması sağlanır.

Örneklere geçelim..

Aşağıdaki örneğimizde, Uretim high-level class tır. Araba ise low-level class tır. Dolayısıyla Uretim class’ı içerisindeki UretimYap() metodu içindeki işlemler Araba class’ındaki ParcalariMonteEt() metoduna bağlıdır. Araba class’ındaki metodlarda yapılacak olan değişiklikler üst class’ı da etkilemektedir. Yani, Araba class’ına herhangi bir metod eklediğimizde gerektiğinde yine Uretim class’ındaki UretimYap() metodunda da metod eklememiz gerekecektir. Yine bir bağımlılık söz konusudur. Dolayısıyla, bu durum Dependency Inversion Prensibi’ne aykırıdır.

Şimdi olması gereken senaryoya geçelim.

Aşağıdaki gibi IUretim interface implement almış olan Motorsiklet ve Otomobil class’larımızı oluşturuyoruz.

Üretimden sorumlu ProductManager class’ımızı oluşturuyoruz ve bu class’ımızı IUretim interface’ine bağlı hale getiriyoruz. Böylece IProduct interface’inden implement olmuş tüm class’lar ProductManager tarafından kullanılabilecektir.

Sonuç olarak, yukarıdaki uygulamamız ile birlikte de Dependecy Inversion Prensibi’ne uygun bir şekilde low-level class’ların high-level class’lar ile olan bağlantısı interface yardımı ile sağlanmış oldu.

Bu şekilde SOLID Yazılım prensipleri’nin beşincisini de incelemiş olduk.

Makalemi beğendiyseniz alkışlamanız beni mutlu edecek.. :)

Yeni yazılarımdan haberdar olmak için beni twitter, linkedin veya medium hesaplarımdan takip edebilirsiniz.

Makalede aklınıza takılan, net olmayan veya belirtmek istediğin bir şey varsa aşağıdaki yorum kısmına yazabilirsiniz. Faydası olması dileğiyle..

--

--