Tasarım Desenleri: Ne Zaman İşe Yarar, Ne Zaman Yük Olur
Tasarım deseni denince akla çoğu zaman kopyalanmayı bekleyen bir kod parçası gelir. Oysa desen kodun kendisi değil, tekrar eden bir çözümün adıdır. Asıl faydası da oradan çıkar: ekipteki herkes aynı yapıya aynı ismi verdiğinde, tartışma her seferinde sıfırdan başlamaz.
Fikir yazılımdan çıkmadı
Desen kavramını 1977'de mimar Christopher Alexander A Pattern Language kitabında ortaya attı. Yaptığı şey basitti: bir yerleşimde tekrar tekrar karşılaşılan mekân problemlerini ad, bağlam, çözüm üçlüsüyle kataloglamak. 1994'te dört yazarın (kısaca Gang of Four) yayımladığı Design Patterns aynı yöntemi nesne yönelimli programlamaya taşıdı.
Bu kökeni bilmek işe yarar, çünkü katalog maddesinin biçimini açıklar. Her desende problem, uygulanacağı bağlam, çözümün yapısı ve sonuçlar başlıkları vardır. Sonuçlar bölümü desenin neyi zorlaştırdığını anlatır ve blog özetlerinde ilk düşen kısım odur. Bir deseni sadece yapısıyla öğrenen kişi, elinde bedeli yazılmamış bir reçete taşır.
Yazılım deseni ile arayüz deseni aynı şey değil
Aynı kelime iki ayrı geleneği anlatıyor ve bunlar sık sık tek bir liste altında toplanıyor. Observer bir nesne ilişkisidir; sonsuz kaydırma ise bir etkileşim alışkanlığı.
Aradaki fark, doğruluğun nasıl sınandığında. Observer'ın yerinde kullanılıp kullanılmadığını koda bakarak tartışabilirsiniz: bağımlılık yönü doğru mu, olay zinciri döngüye giriyor mu. Sonsuz kaydırmanın doğru olup olmadığını ise ancak kullanıcıya bakarak anlarsınız, çünkü orada ölçüt kullanıcının beklentisidir. İki soru farklı yöntemlerle cevaplanır, aynı katalogda durmaları da bu yüzden yanıltıcıdır.
Bazı desenler aslında dilin eksiğini kapatıyor
Gang of Four kitabındaki örnekler C++ ve Smalltalk ile yazılmıştı. Strategy deseni orada bir davranışı sınıfa sarıp dışarıdan enjekte etmeyi anlatır. Sebebi kısmen dildedir: fonksiyonu bir değer gibi dolaştırmak kolay olmadığında, davranışı taşımanın yolu onu nesneye sarmaktır.
Dilinizde birinci sınıf fonksiyon varsa aynı desen çoğu zaman tek parametreye iner. PHP'de 5.3'ten beri anonim fonksiyonlar var; usort'a karşılaştırıcı geçirmek zaten Strategy'dir, üstelik arayüz, üç somut sınıf ve bir fabrika yazmadan. Iterator için de benzeri geçerli: SPL arayüzleri ve foreach dururken deseni elle kurmak, dilin hazır verdiğini görmezden gelmek olur.
Buradan çıkan ölçüt şu: bir deseni uygulamadan önce, çözdüğü problemin dilinizde hâlâ var olup olmadığına bakın.
Her desen bir dolaylılık ekler
Deseni uygulamanın görünmeyen maliyeti okumadır. Fabrika koyduğunuzda, kodu üç ay sonra okuyan kişi bir nesnenin nereden geldiğini anlamak için fazladan iki dosya açar. Bu bedel esneklik gerçekten kullanılıyorsa ödenir; tek bir somut sınıf varsa ödenmez.
En sık görülen hata, problemin ikinci örneği ortaya çıkmadan soyutlamayı kurmak. Üçüncüsünü beklemek eski ve hâlâ iyi bir kural, çünkü iki örnek ortak yapıyı göstermeye yetmez; üçüncüsü genellikle ilk iki örnekte yanlış yere çizdiğiniz sınırı ortaya çıkarır.
Singleton bunun uç örneği (bu deseni bir çözümden çok, çözülmemiş bir bağımlılık probleminin adı sayıyorum). Global erişim verdiği için test sırasında yerine sahte bir nesne koymak zorlaşır, sınıfın gerçek bağımlılıkları imzada görünmez hale gelir. Aynı ihtiyacı bağımlılığı yapıcıdan geçirerek karşılamak hem test edilebilir kalır hem de kimin neye ihtiyaç duyduğunu okunur kılar.
Ekipte deseni işe yarar hale getiren şey
Desen katalogu, ekibin ortak sözlüğü olduğunda değer üretir. Kod incelemesinde "burada Strategy var" demek, on beş satırlık açıklamanın yerine geçer. Bu yüzden desenleri ezberlenecek çözümler olarak değil, adlandırılmış tercihler olarak öğretmek daha verimli.
Pratikte üç soru yetiyor. Bu problem projede kaç kez tekrarladı? Dilin kendisi bunu zaten çözüyor mu? Deseni koyduğumda kodu okuyan kişi kaç dosya fazladan açacak? Üçüne verdiğiniz cevap deseni uygulamayı haklı çıkarmıyorsa, en iyi desen onu yazmamaktır.