SOLID Prensipleri

solid

Günümüzde birçoğumuz C#, Java gibi nesne yönelimli programlama dilleri kullanıyoruz. Peki kullandığımız dillerin gücünden ne kadar faydalanabiliyoruz? Geliştirdiğimiz uygulamalar, zaman içerisinde değişebilecek ihtiyaçlara ne kadar güçlü karşılık verebiliyor? Eğer object oriented programlama yapıyor isek, dünya üzerinde standart kabul edilen 5 temel prensibi bilmemiz gerekiyor.

1. (S)ingle Responsibility Principle

2. (O)pen/Closed Principle

3. (L)iskov ‘s Substitution Principle

4. (I)nterface Segregation Principle

5. (D)ependency Inversion Principle

Solid’e ek olarak Kiss, Yangi, Dry, Reuse Release Equivalence, Common Closure prensipleri de bulunmaktadır.

Şimdi kısaca bu prensiplerin neler olduğundan bahsedelim:

Single Responsibility Principle

Her ne kadar kaliteli kod yazmak için özen göstersek de, çalışma hayatında önümüze sadece kendi geliştirmiş olduğumuz projeler gelmiyor. Tek bir class içerisinde yazılmış binlerce satır kodu okuyup anlamaya çalışmak (belki sadece küçük bir revizyon için) zorunda kalabiliyoruz. Böyle durumlarda ekip arkadaşlarınızla aranızda şöyle konuşmalar geçtiği olur:

+Şu projede biraz optimizasyon yapsak mı?
– Abi çalışıyorsa hiç dokunmayalım.

Projenin içerisindeki her bir yapı, diğer yapılara o kadar bağımlıdır ve yapılan işler o kadar iç içe geçmiştir ki; küçük bir değişikliğin neleri etkileyeceğini kestirmeniz çok zordur ve genelde böyle projeler çöp proje olarak görülür. İçerisindeki class’ları methodları alıp başka bir projede kullanamazsınız.

Eğer tek sorumluluk prensibine uyarsanız bu şekilde binlerce satırlık class’larınız methodlarınız olmaz. Her class’ın, her mothodun sadece tek bir yaptığı iş vardır, böylece bir değişiklik yapmak için sadece bir nedeniniz olmuş olur. Genişleyebilir, tekrar kullanılabilir ve test edilebilir yapılar kurmak için tek sorumluluk ilkesini dikkate almamız gerekir.

Open/Closed Principle 

Açık kapalı prensibi, yazılım geliştirirken kullandığımız varlıkların (class, method vs.) gelişime açık, kodların ise değişime kapalı olması ilkesidir. Örneğin; bir loglama altyapısı oluşturduğunuzu düşünün, Veritabanına ve XML’e kayıt tutuyorsunuz. Daha sonradan Eventloglara da log tutma ihtiyacı hissettiğinizde, sadece Eventloglara kayıt tutan kodları yazmanız yetecek, kodunuzda hiçbir değişiklik yapmadan bu yapı sisteme entegre olacak. Bunun için uygulayacağımız çözüm şu şekilde olabilir:

solid1

solid2

solid22

 

Burada LogTo methoduna ILogger interface’inden implemente olmuş bir class veriyoruz. Yani; daha sonrasında gene ILogger interface’i üzerinden implemente olmuş EventLog isimli bir class yazarsak Logger sınıfı üzerinde hiçbir değişiklik yapmamız gerekmeden, sisteme n sayıda log tutan yapı entegre edebileceğiz.

solid3

Liskov ‘s Substitution Principle

Liskov’un yerine geçme prensibi alt sınıflardan oluşturulan nesnelerin üst sınıfların nesneleriyle yer değiştirdiklerinde aynı davranışı göstermek zorunda olduklarını söyler. Yani; türetilen sınıflar, türeyen sınıfların tüm özelliklerini kullanmak zorundadır. Eğer kullanmaz ise ortaya işlevsiz, dummy kodlar çıkacaktır. Bu durumda üst sınıfta if else blokları kullanarak tip kontrolü yapmamız gerekebilir ve böylelikle Açık Kapalı prensibine de ters düşmüş oluruz.

 

solid33

 

Burada IDeveloper interface’ini implemente eden Junior Developer ve Senior Developer class’ları olduğunu görüyoruz. Ancak proje geliştirilirken bir sorun olduğunu farkettik. Junior Developer’ımız solid ilkelerini kullanamıyor  Yapıyı bu şekilde kurmaya devam edersek ya bu methodun içini boş bırakıcaz ya da  NotImplementedException throw edicez ve ClientApp uygulamamız IDeveloper interface’i üzerinden, IDeveloper’dan implemente olan tiplerini kullanıp Açık Kapalı prensibine bağlı kalabilecekken eğer JuniorDeveloper değil ise SolidIlkeleriniKullan() diyen bir if else bloğuna sahip olucak.

Peki böyle bir durumda çözüm nasıl olabilirdi? Bu yanlış tasarımın önüne nasıl geçeriz?

 

solid3333

 

Class Diagramından da anlaşıldığı gibi Developer sınıfını soyut yaptık ve ISolidKullanabilir isimli bir Interface oluşturduk. Böylelikle Liskov prensibine bağlı kaldığımız gibi, oluştuduğum dll’i kullanan Client Uygulaması da if else bloğuna gerek kalmadan Açık Kapalı prensibine uygun olabilecek.

Interface Segregation Principle

Arayüz ayırım prensibi, bir arayüze gerektiğinden fazla yetenek eklemememiz gerektiği söyler.

 

solid33444

 

Yukarıdaki Diagrama baktığımızda IArac Interface’inde Calistir, Durdur ve KlimayiAc method imzalarının tanımlanmış olduğunuz görüyoruz. Ancak burada bir sorun var. BMV class’ı bütün methodları implemente edebilirken Murat131 de klima özelliği yok.

Böyle bir durumda Arayüz ayırım prensibinin bize önerdiği çözüm şu: Eğer interface’iniz implemente olacağı tüm sınıflarda desteklenemiyorsa Interface’inizi bölün. Bu ilkeye uyarak yapıyı tekrar kuralım:

 

solid334445555

 

Burada IKlimaliArac interface’ini IArac interface’inden türettik. BMV sınıfı IKlimaliArac, Murat131 ise IArac interface’ini implemente ederek Arayüz Ayırma Prensibine uygun bir tasarıma sahip olmuş oldu.

Dependency Inversion Principle

Bağımlılığın ters çevirilmesi ilkesine göre üst seviye sınıflar, modüller, methodlar vs. alt seviyeli sınıflara bağımlı olmamalıdır. Alt sınıflarda yapılan değişiklikler üst sınıfları etkilememelidir.

 

solid6666

 

Oluşturduğum örnekte üst sınıf olan Encryption, alt sınıfları olan XMLReader ve TextReader’dan belirli bir formatta dönen veriyi şifrelemektedir. Reader sınıfları birer nesne olarak Encryption sınıfının içinde kullanılmakta/bağımlı olarak çalışmaktadır. Alt sınıf olan Reader sınıflarından birini uygulama dışında tuttuğumuz anda Encryption sınıfı hata verecektir. Reader sınıflarının ReadAll methodlarında yapacağımız bir kod değişikliği Encryption sınıfının işlevselliğini/kurgusunu rahatlıkla bozabilecektir.
Burada, üst sınıfın alt sınıflara olan bağımlılığı nasıl ters çevirebiliriz? Örneğini verdiğimde ters çevirmekten neyi kast ettiğimizi daha rahat anlayabileceğiz.
solid66669999
Yeni oluşturmuş olduğumuz yapıda, Encryption sınıfının XMLReader ve TextReader sınıflarına olan bağımlılığı, araya IReader interface’i koyularak ters çevrilmiştir. Alt seviyeli olan Reader sınıfları IReader interface’ine bağımlı durumdadır ve içlerinde yapılan değişiklikler Encryption sınıfını kesinlikle etkilemeyecektir. IReader interface’ini implemente edecek FileReader gibi bir sınıf, Encryption üzerinde hiçbir değişiklik yapılmadan kullanılabileceği bir tasarıma da uygun hale gelmiştir.
Diğer Prensipler :
Kiss Principle : Keep it Simple Stupid
Bir problemin birçok durumda birden fazla çözümü vardır ve biz yazılımcılar kendi bilgi/birikimlerimizi en iyi yansıtan, en karmaşık kod yapısını kullanmaya eğilimli olabiliyoruz. Kiss prensibine göre çözümlerimiz mümkün oldunca sade/anlaşılır olmalı. En basit ve en optimum çözüm yöntemini seçerek uygulama geliştirmeliyiz.
Yangi Principle : You Aren’t Gonna Need It!
Yangi prensibine göre, “o an” ihtiyacımız olmayan kodları sisteme dahil etmemeliyiz, ileride eklenebilecek özellikler hakkında öngürüde bulunup ek geliştirmeler yapmamalıyız.
Dry Principle : Don’t Repeat Yourself
Proje içerisinde aynı kodu tekrar tekrar yazıyorsanız dry prensibine uymuyorsunuz demektir. Tekrarlı kod yapısında değişiklik olması, kullandığınız her yerde tek tek değiştirmenizi gerektirebilir ve ayrıca sistemi gereksiz yere karmaşık hale getirecektir. Böyle bir durumda, tekrarlı kodları merkezileştirecek bir çözüm üretmemiz gerekmektedir.
Reuse-Release Equivalence Principle
Sistemde kullanılan paketler/namespace’ler arasındaki bağımlılıkları yönetmek “tekrar kullanılabilir” yapılar kurmakla mümkün olur.
Common Closure Principle
Ortak Kapama Prensibi, Single Responsibility’nin namespace’ler için uyarlanmış halidir. Aynı sebepten değişebilecek sınıflar aynı namespace altında bulunmalıdır. Böylilikle sistemde oluşabilecek değişikliklerin tüm sistemi etkilemesinin önüne geçilmesi amaçlanır.

 

 

Kaynak : http://tarikkaygusuz.com

Bir cevap yazın

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

*
*