SOLID Prensipleri

SOLID prensipleri Robert C. Martin tarafından önerilmiştir. Java programlama dilinde, yazılım tasarımının beş temel prensibini ifade eder ve yazılımın sürdürülebilirliğini, esnekliğini ve bakımını artırmayı hedefler. Nesne yönelimli programlama (OOP) kavramlarına dayanır ve yazılım geliştirme sürecinde tasarım kalitesini yükseltmeye yardımcı olur.
1. SİNGLE RESPONSİBİLİTY PRİNCİPLE (Tek Sorumluluk Prensibi):
Bir sınıfın yalnızca bir sorumluluğu olmalıdır. Sınıfın değişmesi gereken tek bir neden veya sorumluluk olmalıdır. Bu prensip, sınıfların ve metotların tek bir amaç için odaklanmasını sağlar ve daha kolay anlaşılır, sürdürülebilir ve test edilebilir kod üretmeye yardımcı olur. Bundan farklı olarak bir sınıfın birden fazla sorumluluğu olduğunu varsaydığımız da herhangi bir sorun ortaya çıkması durumunda veya bir sorumluluk değiştiğinde diğerleri etkilenebilir.
Single Responsibility Principle amacı, bir sınıfın veya modülün tek bir işlev için odaklanmasını sağlamaktır. Bu, kodun daha temiz, daha okunabilir ve daha sürdürülebilir olmasını sağlar. Bir sınıfın veya modülün birden fazla sorumluluğu olduğunda, her bir sorumluluk değiştiğinde diğer sorumluluklar üzerinde etki yaratabilir. Bu da kodun karmaşıklaşmasına, hataların ortaya çıkmasına ve bakımın zorlaşmasına neden olabilir.
Single Responsibility Principle uygulanması, daha küçük ve daha özgün sınıfların oluşturulmasını teşvik eder. Bu şekilde, her sınıf yalnızca belirli bir işlevi yerine getirir ve bu işlevi yerine getirmek için gerekli olan kodu içerir. Bu da kodun anlaşılmasını ve değiştirilmesini kolaylaştırır.
Örneğin, bir müşteri bilgilerini tutan bir sınıfı ele alalım. Bu sınıfın sorumluluğu, müşteri verilerini veritabanında saklamak, müşteri verilerini okumak, güncellemek veya silmek olabilir. Ancak, aynı sınıfın aynı zamanda müşteri e-postalarını göndermekle veya müşteri faturalarını oluşturmakla ilgilenmesi yanlış olur. Bu durumda, Single Responsibility Principle uygulamak için müşteri verilerini saklamak ve yönetmek için bir sınıf oluştururken, müşteri e-postalarını göndermek veya faturaları oluşturmak için başka sınıflar oluşturulmalıdır.
Bu prensibi uygularken, her sınıfın ve modülün tek bir sorumluluğu olmasına dikkat etmek önemlidir. Böylece, kodlar daha modüler, esnek ve sürdürülebilir olur.
2. OPEN/CLOSED PRİNCİPLE (Açık/Kapalı Prensibi):
Bir yazılım bileşeni (sınıf, modül, metod) değiştirilebilir olmalı, ancak mevcut işlevselliği değiştirmemelidir. Yeni bir işlevselliği eklemek için mevcut kodu değiştirmek yerine, genişletilebilirlik sağlamak için alt sınıflar veya arayüzler kullanılmalıdır. Bu prensip, yazılımın değişikliklere kapalı, ancak genişlemeye açık olmasını sağlar.
Open/Closed Principle temel amacı, yazılım bileşenlerinin değişikliklere karşı dirençli olmasını sağlamaktır. Bir bileşeni değiştirmek için mevcut kodu değiştirmek, diğer bağımlı bileşenleri de etkileyebilir ve bu durum olumsuz sonuçlara yol açabilir. Open/Closed Principle, yeni işlevselliği eklemek için bileşenin davranışını değiştirmeden genişletme yapmayı teşvik eder.
Open/Closed Principle uygulamak için genellikle abstraction (soyutlama) ve inheritance (kalıtım) kullanılır. Bileşenler, soyut sınıflar veya arayüzler yoluyla tanımlanır ve daha spesifik alt sınıflar veya uygulamalar tarafından genişletilebilir. Böylece, mevcut kod yapısı değiştirilmeden yeni işlevselliği eklemek mümkün hale gelir.
Örneğin, bir mesaj gönderme sistemi uygulamasını ele alalım. Başlangıçta sadece e-mail gönderme bir sınıfımız var. Ancak daha sonra SMS ve WhatsApp mesajları göndermesi de istenildiğinde, Open/Closed Principle prensibini uygulamak önemli hale gelir. Bunun için, e-mail sınıfını değiştirmek yerine, her mesaj yöntemi için bir alt sınıf oluşturulur ve soyut bir mesaj sınıfı veya arayüzü kullanılabilir. Böylece, yeni bir mesaj gönderme yöntemi eklemek için mevcut kodu değiştirmek zorunda kalmadan, sadece yeni bir alt sınıf oluşturarak işlevselliği genişletebiliriz.
Open/Closed Principle yazılımın esnekliğini ve genişletilebilirliğini artırır. Mevcut kodun değişikliklere kapalı olması, hata riskini azaltır ve varolan işlevselliğin istikrarını ve guvenirligini korur. Yeni işlevselliğin eklenmesi ise kodun genişletilmesini kolaylaştırır ve daha az olumsuzluklarla karşılaşılabilir.
3. LİSKOV SUBSTİTUTİON PRİNCİPLE (Liskov Yerine Geçme Prensibi):
Bir üst sınıf yerine, onun alt sınıfları kullanılabilmelidir. Yani, bir sınıfın alt sınıfları, üst sınıfın tüm davranışlarını korumalı ve değiştirmemelidir. Bu prensip, polimorfizmi destekler ve bir sınıfın türetilmiş sınıflarının, kullanıldıkları yerlerde sorunsuz bir şekilde yerine geçebilmesini sağlar.
Liskov Substitution Principle programlama dillerindeki polimorfizm kavramının temelidir. Polimorfizm, bir nesnenin farklı tipleriyle aynı arabirimi kullanabilme yeteneğini ifade eder.
Liskov Substitution Principle anlamak için “ alt sınıfları üst sınıfların yerine koyabilirsiniz” ifadesini ele alalım. Bir üst sınıfı kullanan bir program veya fonksiyon, bu üst sınıfın yerine, türetilmiş sınıflardan birini koyarak aynı şekilde çalışmalıdır. Yani, üst sınıfın sağladığı davranışlar alt sınıflar tarafından korunmalı ve beklenen sonuçları üretmelidir.
Liskov Substitution Principle amacı, kodun güvenilirliğini ve doğruluğunu sağlamaktır. Eğer bir türetilmiş sınıf, üst sınıfın belirlediği davranışları yerine getirmez veya beklenen sonuçları üretmezse, bu durum beklenmedik hatalara, yanlış sonuçlara veya programın doğru çalışmamasına neden olabilir.
Liskov Substitution Principle sağladığı faydalar:
1. Kodun yeniden kullanılabilirliği: Alt sınıfların üst sınıfların yerine geçebilir olması sayesinde kodun yeniden kullanılabilirliğini artırır. Bir üst sınıfın sağladığı davranışları koruyarak yeni alt sınıflar oluşturmak mümkün olur.
2. Esneklik ve genişletilebilirliği: Yazılımın esnek ve genişletilebilir olmasını sağlar. Var olan bir üst sınıfın yerine yeni bir türetilmiş sınıfı koyarak sistemi genişletebilirsiniz, böylece sistemi değiştirmek veya mevcut kodu değiştirmek zorunda kalmayız.
3. Kodun anlaşılabilirliği: Kodun anlaşılabilirliğini artırır. Bir üst sınıfın kullanıldığı herhangi bir yerde, o sınıfın türetilmiş sınıflarıyla değiştirilmesi durumunda beklenen sonuçların aynı olacağını bilmek, kodu daha kolay anlamanızı sağlar.
Liskov Substitution Principle uygulamak için dikkat edilmesi gerekli durumlar:
1. Davranışların yerine getirilmesi: Alt sınıf, üst sınıfın tanımladığı tüm davranışları yerine getirmelidir. Yani, alt sınıfın tüm üst sınıf metodlarını uygulaması ve bu metotların beklenen sonuçları üretmesi gerekmektedir. Bu şekilde, alt sınıf üst sınıfın yerine geçebilir ve aynı şekilde kullanılabilir olur.
2. Önkoşulların korunması: Alt sınıf, üst sınıfın davranışlarını değiştirmeden veya kısıtlamalarını gevşetmeden kullanmalıdır. Yani, üst sınıfın metotlarının giriş koşullarını (preconditions) korumalıdır. Alt sınıf, üst sınıfın belirlediği önkoşulları yerine getirmelidir.
3. Sonrası koşulların korunması: Alt sınıf, üst sınıfın davranışlarını dönüş değerleri ve sonrası koşullar (postconditions) açısından da korumalıdır. Yani, üst sınıfın metotlarından beklenen sonuçları aynen üretmelidir ve üst sınıfın belirlediği sonrası koşulları sağlamalıdır.
4. İstisna durumların uyumu: Alt sınıf, üst sınıfın metotlarında belirtilen istisna durumları (exceptions) uyumlu bir şekilde işlemelidir. Yani, alt sınıfın uygun istisna durumlarını oluşturmalı ve bu istisnaların üst sınıfın beklediği şekilde yönetilmesini sağlamalıdır.
4. INTERFACE SEGREGATİON PRİNCİPLE (Arayüz Ayrımı Prensibi):
İstemciler, kullanmadıkları metodlara bağımlı olmamalıdır. Kullanıcılar, ihtiyaç duymadıkları metotlara zorunlu olarak bağımlı olmamalıdır. Bunun yerine, geniş arayüzler yerine özel ihtiyaçlara yönelik daha küçük ve spesifik arayüzler kullanılmalıdır. Bu prensip, sınıfların yalnızca ihtiyaç duydukları metotları uygulamasını sağlar ve gereksiz bağımlılıkları azaltır.
Interface Segregation Principle amacı, karmaşık ve büyük arayüzleri daha küçük ve özelleştirilmiş arayüzlerle parçalamaktır. Böylece, sınıfların sadece ihtiyaç duydukları metotları uygulaması sağlanır ve gereksiz bağımlılıklar önlenir. Bu prensip, bir sınıfın sadece kendi sorumluluk alanına giren işlemleri yapmasını ve diğer gereksinimleri taşımamasını sağlar.
Interface Segregation Principle temel prensipleri:
1. Arayüzlerin mümkün olduğunca spesifik olması: Bir arayüz, sadece belirli bir işlevselliği temsil etmelidir. Bu, arayüzde yalnızca ilgili metotları bulundurarak gereksiz metotların ve bağımlılıkların önlenmesini sağlar.
2. İstemciye özelleştirilmiş arayüzler sağlama: İstemci, sadece ihtiyaç duyduğu metotları içeren özelleştirilmiş bir arayüz kullanmalıdır. Bu, istemcinin sadece ilgili metotlara erişerek gereksiz bağımlılıklardan kaçınmasını ve sınıfların gereksinimlerine uygun arayüzleri kullanmasını sağlar.
3. Bağımlılıkları azaltma: Sınıflar arasındaki bağımlılıkları azaltmayı hedefler. Bir sınıfın ihtiyaç duymadığı metotları içeren bir arayüzü uygulaması gerekmediği için, gereksiz bağımlılıkların önlenmesine yardımcı olur.
Interface Segregation Principle uygulanması, yazılımın esnekliğini ve sürdürülebilirliğini artırır. Sınıfların ihtiyaç duymadığı metotlardan kaçınarak, kodun daha anlaşılır ve sınıfların sorumlulukları daha net bir şekilde belirlenir. Ayrıca, değişikliklerin sadece ilgili arayüzlerde yapılabilmesi, kodun daha az etkilenmesini ve hataların daha kolay tespit edilmesini sağlar.
5.DEPENDENCY INVERSİON PRİNCİPLE (Bağımlılığı Tersine Çevirme Prensibi)
Yüksek seviyeli modüller, düşük seviyeli modüllere bağımlı olmamalıdır. Bu prensip, bağımlılıkların soyutlamalara ve arayüzlere dayandırılması gerektiğini vurgular. Yani, somut sınıflar yerine soyut sınıflara ve arayüzlere bağımlılık oluşturulmalıdır.
Dependency Inversion Principle temel amacı, sınıflar arasındaki sıkı bağımlılıkları gevşetmek ve kodun daha esnek, yeniden kullanılabilir ve bakımını kolay hale getirmektir. Bu prensip, aşağıdaki iki ana kurala dayanır:
1. Yüksek seviyeli modüller, düşük seviyeli modüllere bağımlı olmamalıdır: Yüksek seviyeli modüller, düşük seviyeli modüllerin ayrıntılarına doğrudan bağımlı olmamalıdır. Bunun yerine, yüksek seviyeli modül, soyutlamalara (abstractions) bağımlı olmalı ve düşük seviyeli modülün nasıl uygulandığından bağımsız olmalıdır.
2. Soyutlamalar, ayrıntılara bağımlı olmalıdır: Sınıflar, soyutlamalara (abstractions) bağımlı olmalıdır. Yani, yüksek seviyeli modülün düşük seviyeli modülle iletişim kurarken somut uygulamalara değil, arayüz veya soyut sınıflara bağımlı olması gerekmektedir. Bu, sınıfların daha esnek ve yeniden kullanılabilir olmasını sağlar.
Dependency Inversion Principle uygulanması için genellikle bir arayüz veya soyut sınıf kullanılır. Yüksek seviyeli modül, bu soyutlamayı kullanarak düşük seviyeli modülle iletişim kurar. Böylece, düşük seviyeli modülün uygulama ayrıntılarından bağımsız olarak yüksek seviyeli modülü değiştirebilir veya başka bir modülle değiştirilebilir. Kodun test edilebilirliğini, esnekliğini ve genişletilebilirliğini artırır. Ayrıca, bağımlılıkların tersine çevrilmesi, yeni gereksinimlerin kolayca karşılanabilmesini sağlar ve sınıfların daha az bağımlı olmasını ve yeniden kullanılabilirliğini artırır.
Dependency Inversion Principle temel prensipleri:
1. Yüksek seviyeli modüller, düşük seviyeli modüllere bağımlı olmamalıdır:
- Yüksek seviyeli modüller, genel işlevselliği temsil eder ve daha soyut bir seviyededir.
- Düşük seviyeli modüller, daha spesifik işlevselliği uygular ve daha somut bir seviyededir.
- Dependency Inversion Principle yüksek seviyeli modüllerin doğrudan düşük seviyeli modüllere bağımlı olmamasını önerir.
- Bunun yerine, yüksek seviyeli modül, düşük seviyeli modülün nasıl uygulandığından bağımsız olarak çalışabilmek için soyutlamalara (arayüz veya soyut sınıflar) bağımlı olmalıdır.
2. Soyutlamalar, ayrıntılara bağımlı olmalıdır:
- DIP, sınıfların soyutlamalara bağımlı olmasını önerir.
- Soyutlamalar, yüksek seviyeli modülün düşük seviyeli modülle iletişim kurarken kullanacağı arayüzleri veya soyut sınıfları temsil eder.
- Bu sayede, yüksek seviyeli modül, soyutlamalar üzerinden düşük seviyeli modülle iletişim kurar ve somut uygulama ayrıntılarından bağımsız olur.
-Bu, yüksek seviyeli modülün düşük seviyeli modülü değiştirebilmesini veya başka bir modülle değiştirilebilmesini sağlar.