Skip to main content
•6 dk okuma

S-O-L-I-D Prensipleri Nedir?

Mustafa Kürşad BaşerMustafa Kürşad BAŞER
••
S-O-L-I-D Prensipleri Nedir?
SOLID Principles, by Mustafa K. Başer

SOLID Prensipleri

Uzun ömürlü her kod tabanı er geç aynı dört şikayete varır. Değiştirmesi zordur. Bir yeri düzeltince başka yeri bozulur. Parçaları başka bir projede kullanılamaz. Ve bakımı pahalıdır. Yıllar içinde bu dertlere karşı geliştirilen yaklaşımların bir kısmı standartlaştı, bugün Tasarım Kalıpları (Design Patterns) ve tasarım prensipleri dediğimiz külliyat böyle oluştu.

SOLID de bu külliyatın bir parçası. Prensipleri 2000 tarihli "Design Principles and Design Patterns" yazısında bir araya getiren Robert C. Martin’dir. Beşinin baş harflerinden SOLID kısaltmasını türeten ise Michael Feathers olmuştur. Sırayla bakalım.

SOLID’in vaadi tek cümlede toplanabilir: değişikliğin maliyetini düşürmek. Yeni bir özellik istendiğinde sistemi baştan tasarlamak zorunda kalmamak, bir kütüphaneyi ya da veri tabanını değiştirirken kodun yarısını yeniden yazmamak. Beş prensip de aynı hedefe farklı açılardan saldırıyor. Şunu baştan söyleyeyim: bunlar kanun değil, ödünleşme. Her birinin bedeli önden düşünmek ve fazladan soyutlama.

S-O-L-I-D Açılımı Nedir?

  • Single Responsibility Principle (Tek Sorumluluk Prensibi)
  • Open-Closed Principle (Açık-Kapalı Prensibi)
  • Liskov’s Substitution Principle (Liskov'un Yerine Geçme Prensibi)
  • Interface Segregation Principle (Arayüz Ayrımı Prensibi)
  • Dependency Inversion Principle (Bağımlılığı Tersine Çevirme Prensibi)

Single Responsibility Principle (Tek Sorumluluk Prensibi) Nedir?

Bu prensip tek şey söylüyor: bir sınıfın değişmesi için tek bir sebep olmalı. Robert C. Martin sonradan bunu "bir sınıf yalnızca tek bir aktöre karşı sorumlu olmalıdır" diye netleştirdi. Yani sınıfı kimin talebi değiştiriyorsa, sorumluluk odur.

Bir e-ticaret uygulamasındaki "Sepet" sınıfını düşünün. İçinde ürün ekleme ve toplam hesaplama var. Bir de PDF fatura üretimi ve ödeme sağlayıcısına istek atma var. Bu sınıf artık üç ayrı ekibin talebiyle değişiyor: satış, muhasebe, ödeme. Muhasebenin istediği ufak bir fatura formatı değişikliği, sepet toplamını bozma riski taşıyor. Fatura ve ödeme kendi sınıflarına çıktığında o risk ortadan kalkıyor.

Kazanç günlük işte ortaya çıkıyor. Bir hatayı ararken bakacağınız dosya küçülür, testler kısalır. Sepet sınıfını test etmek için sahte bir ödeme sağlayıcısı ayağa kaldırmanız gerekmez.

Open-Closed Principle (Açık-Kapalı Prensibi)

Prensibin klasik ifadesi şöyle: bir modül genişletmeye açık, değişikliğe kapalı olmalı. Yani yeni davranış eklerken çalışan kodu kurcalamak zorunda kalmamalısınız. Kapalı olan mevcut kodun gövdesi, açık olan ise ona yeni parça takabilme imkanı.

Ödeme sistemi bunun iyi bir örneği. Kredi kartıyla ödeme alan bir sınıfınız var, havale desteği eklemeniz isteniyor. İlk refleks mevcut sınıfa bir if bloğu eklemek. Üçüncü ödeme yönteminde o if zinciri okunmaz hale gelir ve her yeni yöntem, çalışan kredi kartı akışını da riske atar.

Ödeme yöntemini bir arayüz arkasına aldığınızda havale yeni bir sınıf olarak eklenir. Kredi kartı koduna hiç dokunulmaz.

Bedeli de var. Bu esnekliği baştan kurmak, gerçekten ikinci bir ödeme yöntemi geleceğini bilmeyi gerektirir. Gelmezse elinizde tek uygulaması olan gereksiz bir arayüz kalır. Pratik kural: ikinci vaka gelene kadar bekleyin, üçüncüsünü beklemeyin.

Liskov’s Substitution Principle (Liskov'un Yerine Geçme Prensibi)

Prensip adını Barbara Liskov’dan alıyor, kökeni 1987’de verdiği bir konferans konuşmasına dayanıyor. Özü de şöyle. Bir üst sınıfı bekleyen kodun içine alt sınıfı koyduğunuzda, o kod bozulmamalı. Alt sınıf, üst sınıfın verdiği sözleri tutmak zorunda.

En bilinen örnek Dikdörtgen ve Kare. Matematikte kare bir dikdörtgendir, o yüzden Kare sınıfını Dikdörtgen’den türetmek doğal görünür. Ama dikdörtgenin genişliğini 5, yüksekliğini 4 yapıp alanının 20 çıkmasını bekleyen bir kod, eline Kare geçtiğinde 16 ya da 25 görür.

Kalıtım hiyerarşisi geçerlidir, davranış sözü tutulmamıştır. LSP’nin uyardığı şey tam olarak bu: "bir çeşididir" ilişkisi sınıflandırmada değil, davranışta aranır.

Pratikte bu prensip iki şeyi yasaklar: alt sınıfın devraldığı bir metodu "burada geçerli değil" diye boş bırakmasını ve beklenmedik bir istisna fırlatmasını. Böyle bir kod yazmak zorunda kaldıysanız sorun alt sınıfta değil, hiyerarşinin kendisindedir.

Kazanç, üst sınıf tipiyle çalışan kodun alt sınıfları hiç tanımadan güvenle işleyebilmesi. O güven kaybolduğu anda kod her yerde "acaba hangi alt sınıf geldi" diye tip kontrolü yapmaya başlar.

Interface Segregation Principle (Arayüz Ayrımı Prensibi)

ISP, bir sınıfın kullanmadığı metotlara bağımlı kalmaması gerektiğini söyler. Şişkin bir arayüz, onu uygulayan her sınıfa hiç çağırmayacağı metotları da yazma yükü bindirir.

Bir otel rezervasyon sistemi yazdığımızı düşünelim. Tek bir Rezervasyon arayüzü tanımladık, içine odaAra, rezervasyonYap, fiyatGuncelle, odaEkle ve raporAl metotlarını koyduk. Müşteri tarafındaki sınıf bu arayüzü uyguladığında, hiç kullanmayacağı fiyatGuncelle ve odaEkle metotlarını da yazmak zorunda kalıyor. Yönetim paneli ise odaAra ile ilgilenmiyor ama onu da taşıyor.

Çözüm arayüzü bölmek. Müşteri tarafı için arama ve rezervasyon, yönetim tarafı için oda ve fiyat işlemleri. Her sınıf yalnızca gerçekten kullandığı sözleşmeye bağlı kalır. Arayüzlerden birine yeni metot eklendiğinde diğer taraf hiç etkilenmez.

Asıl kazanç okunabilirlik değil, değişimin yarıçapı. Dar bir arayüz, tek bir değişikliğin kaç sınıfı yeniden derlemeye ve yeniden test etmeye zorlayacağını küçültür.

Dependency Inversion Principle (Bağımlılığı Tersine Çevirme Prensibi)

Beşinci ve sonuncu prensip, aynı zamanda en çok yanlış anlaşılanı. Adındaki "tersine çevirme", bağımlılık okunun yönünü değiştirmekten geliyor.

Kural iki cümle. Yüksek seviyeli modüller düşük seviyeli modüllere bağımlı olmamalı, ikisi de soyutlamalara bağımlı olmalı. Ve soyutlamalar detaylara değil, detaylar soyutlamalara uymalı. Genelde atlanan kritik nokta ise sahiplik. Arayüzün tanımı onu kullanan yüksek seviyeli tarafa aittir, uygulayan tarafa değil.

Bunu somutlaştıran şey, arayüzün hangi pakette durduğu. Veri tabanı katmanının yanında duran bir arayüz, adı ne olursa olsun hala veri tabanının şartlarını dayatıyordur.

Örnek verelim. Sipariş işleyen bir modülünüz var ve içinde doğrudan PostgreSQL bağlantısı açıyor. Yarın veri tabanı değişse ya da testlerde gerçek bir veri tabanı istemeseniz, modülü baştan yazmanız gerekir. Bunun yerine modül bir SiparisDeposu arayüzü tanımlar, PostgreSQL uygulaması o arayüzü karşılar. Testte sahte bir uygulama vermek yeterli olur, sipariş mantığına hiç dokunulmaz.

Bu prensip sık sık bağımlılık enjeksiyonuyla karıştırılır, ama aynı şey değiller. Enjeksiyon bir tekniktir. DIP ise okun hangi yöne bakacağına dair bir karardır.

Sonuç

Beş prensibi tek tek gezdik. Hepsinin derdi aynıydı: değişiklik geldiğinde dokunulması gereken yer sayısını azaltmak.

Kısa hatırlatma: Single Responsibility, bir sınıfın değişmesi için tek bir sebep olmasını ister. Open-Closed, genişletmeye açık ama değişikliğe kapalı tasarımı önerir. Liskov Substitution, alt sınıfın üst sınıfın yerine sorunsuz geçebilmesini şart koşar. Interface Segregation, sınıfları kullanmadıkları metotlara bağımlı bırakmamayı söyler. Dependency Inversion ise yüksek seviyeli modülün düşük seviyeliye değil, ikisinin birden soyutlamaya bağımlı olmasını ister.

Son bir not. Bu prensipleri her yere uygulamak diye bir şey yok. Yüz satırlık bir script’i beş prensibe uydurmaya çalışmak, çözdüğünden fazla karmaşıklık üretir. Prensipler kod büyüdükçe ve değişiklik sıklaştıkça karşılığını verir. Ne zaman uygulanacağını bilmek, tanımlarını ezberlemekten çok daha değerli.

Referanslar

  • Martin, R. C. (2008). Clean Code: A Handbook of Agile Software Craftsmanship. Prentice Hall.
  • Freeman, E., Robson, E., Bates, B., & Sierra, K. (2004). Head First Design Patterns. Sebastopol, CA: O'Reilly Media.
  • Fowler, M. (n.d.). "SOLID Principles." martinfowler.com

Bu yazıyı paylaş

Link kopyalandı!
Mustafa Kürşad Başer
Yazar

Mustafa Kürşad Başer

Kıdemli Yazılım Mühendisi

Karmaşık sorunlara zarif çözümler üretmekten keyif alan, tutkulu bir yazılım mühendisi. Kodlamanın ötesinde, teknoloji, sanat ve insan bilincinin kesişim noktalarını keşfetmekle derinden ilgileniyor.