SOLID -Open/Closed Principle
Open/Closed Principle, Robert C. Martin tarafından tanımlanan nesne yönelimli yazılım geliştirme için beş tasarım ilkesinden biridir .
- Single Responsibility Principle
- Open/Closed Principle
- Liskov Substitution Principle
- Interface Segregation Principle
- Dependency Inversion Principle
Bu tasarım ilkelerinin tamamı geniş bir şekilde kullanılmaktadır ve tüm deneyimli yazılım geliştiricileri bunlara aşina olmalıdır. SOLID ilkelerini öğrenmeden önce birkaç yıl boyunca bir yazılım geliştiricisi olarak çalışıyordum ve iş arkadaşlarımın bana iyi bir kod yazmayı öğrettikleri kuralları ve ilkeleri tarif ettiklerinde çabucak kabul ettim. Yani, onları isimle bilmeseniz bile, onları zaten kullanıyor olabilirsiniz.
Ancak bu, SOLID ilkeleri hakkında konuşmamamız ve öğrenmememiz gerektiği anlamına gelmez. Bu yazıda, Open/Closed Principle odaklanacağım ve ilerideki makalelerde diğer ilkeleri açıklayacağım.
Open/Closed Principle Tanımı
Robert C. Martin bu ilkeyi “nesne odaklı tasarımın en önemli ilkesi” olarak kabul etti. Ama onu tanımlayan ilk kişi değildi. Bertrand Meyer, 1988'de Object-Oriented Software Construction adlı kitabında yazdı . Open/Closed ilkesini şöyle açıkladı:
“Yazılım öğeleri (classes, modules, functions, vb.) Uzantıya açık olmalı, ancak değişiklik için kapatılmalıdır.”
Bu ilkenin genel fikri mükemmel. Varolan kodu değiştirmeden yeni işlevsellik ekleyebilmeniz için kodunuzu yazmanızı söyler. Bu, sınıflarınızdan birinde bir değişikliğin de, tüm bağlı sınıflara uyum sağlamanızı gerektiren durumları önler. Ne yazık ki, Bertrand Mayer bu hedefe ulaşmak için miras kullanmayı önermektedir ama Bertrand Mayer önermesinden bahsetmeyeceğim araştırabilirsiniz.
Ancak yıllar boyunca öğrendiğimiz ve diğer yazarların büyük ayrıntılarda açıkladığı gibi, örneğin Robert C. Martin, SOLID ilkeleri veya Etkin Bloğu hakkındaki Joshua Bloch hakkındaki makalelerinde, miras sınıfları uygulamaya bağlıysa sıkı bir bağlanma getirmektedir.
Bu yüzden Robert C. Martin ve diğerleri Open/Closed İlkeyi yeniden tanımladılar . Üst sınıflar yerine interface yapılar kullanır ve bunları kullanan kodu değiştirmeden kolayca değiştirebileceğiniz farklı uygulamalara izin verir. Interface değişiklikler için kapatılmıştır ve yazılımınızın işlevselliğini genişletmek için yeni uygulamalar sağlayabilirsiniz.
Bu yaklaşımın ana faydası, bir arabirimin, gevşek bağlılığı mümkün kılan ek bir soyutlama seviyesi getirmesidir. Bir arayüzün uygulamaları birbirinden bağımsızdır ve herhangi bir kodu paylaşmaya gerek yoktur. Bir arabirimin iki uygulamasının bazı kodları paylaştığını düşünürseniz, ya interface veya abstract class kullanabilirsiniz .
Küçük ve basit bir örnek ile konuyu açıklayalım.
Bir banka, birçok müşteri tipinin ihtiyaçlarını karşılayan çeşitli tasarruf hesapları (Maaş Tasarrufu, Düzenli Tasarruf vb.) Sunmaktadır. Bir bankanın farklı kurallar seti vardır ve her tasarruf hesabı türü, faizi hesaplamak için farklı bir kurallar kümesine sahiptir. Bir hesabın faizini hesaplamak için, geliştiriciler, ilgiyi hesaplamak için aşağıdaki sınıfı geliştirdiler.
Prensibe uymayan kod bu şekilde yazarız.
public class SavingAccount
{
public decimal CalculateInterest(AccountType accountType)
{
if(AccountType =="Regular")
{
Interest = balance * 0.4;
if(balance < 1000) interest -= balance * 0.2;
if(balance < 50000) interest += amount * 0.4;
}
else if (AccountType =="Salary")
{
Interest = balance * 0.5;
}
}
}
SavingAccount sınıfının CalculateInterest yöntemi, Regular ve Salary gibi hesap türüne dayalı bir hesaplama yapar.
Bu nedenle, uygulama Open/Closed ilkeyi takip etmemektedir çünkü eğer yarın banka yeni bir SavingAccount türünü tanıtıyorsa, yeni hesap türü için yeni bir durum eklemek için bu yöntemi değiştirmek için bir gereklilik vardır. Örneğin, banka bu hesap türüne olan ilginin hesaplanması için yeni bir koşul gerektiren bir “Çocuk Tasarruf Hesabı türü” getiriyorsa. Bu, yöntemin her zaman değişiklik için açık olduğu anlamına gelir.
Burada dikkat edilmesi gereken bir şey de, yöntemin Tek Sorumluluk İlkesini takip etmemesidir. Buradaki yöntem, birden fazla tür için ilgi hesaplamak gibi, birden fazla şey yapıyor.
İlkenin Nasıl Uygulanacağı Devralma, Open Closed İlkenin uygulanmasının sadece bir yoludur. Miras, mevcut bir sınıfın işlevselliğinin genişletilmesine izin veren sadece Nesneye Dayalı Tasarım (OOD) temel sütunu olduğu için.
Open Closed İlkeyi uygulamak için, işlevselliği genişletmek istediğinizde arayüzü, soyut bir sınıfı, soyut yöntemleri ve sanal yöntemleri kullanabilir.
Dolayısıyla, bir Tasarruf Hesabı'nın önceki problemi aşağıdaki gibi çözülebilir.
interface ISavingAccount
{
decimal CalculateInterest();
}
public class RegularSavingAccount : ISavingAccount
{
public decimal CalculateInterest()
{
Interest = balance * 0.4;
if (balance < 1000) interest -= balance * 0.2;
if (balance < 50000) interest += amount * 0.4;
}
}
public class SalarySavingAccount : ISavingAccount
{
public decimal CalculateInterest()
{
Interest = balance * 0.5;
}
}
Önceki kodda, IsavingAccount'tan devralınan iki yeni sınıf, RgularSavingAccount ve SalarySavingAccount oluşturulur.
Bu yüzden, banka tarafından eklenen yeni bir hesap varsa, çıkmakta olan sınıfların mantığını değiştirmeye gerek yoktur, sadece bir interface miras alarak işlevselliği artırın.
Son olarak, mevcut örnek, mevcut uygulanan mantığı değiştirmeye gerek olmadığı ve yeni mantığın eklenmesine olanak tanıdığı için Open Closed İlkeyi uygular.
Ve önceki örnek aynı zamanda Single Responsibility Principle uygular çünkü her sınıf ya da işlev sadece bir görevi yerine getirir.
Not: Burada sadece bir örnek olarak bir arayüz oluşturulmuştur. Yeni bir tasarruf hesabı türü tarafından uygulanan soyut bir SavingAccount sınıfı olabilir.
Özet:
Bu dizinin önceki yazısında Single Responsibility İlkesine daha yakından baktıktan sonra , şimdi Open Closed İlkesini tartıştık. Robert C. Martin tarafından açıklanan beş KATI tasarım ilkesinden biridir. Varolan kodu değiştirmeden uygulamanızın işlevselliğini uyarlamanızı sağlamak için arabirimlerin kullanımını destekler.