Bridge Design Pattern
Selam dostlar!
Yapısal Tasarım Desenlerini konu alan yazılarımıza, Bridge Design Pattern deseni ile devam ediyoruz. Daha önceki yazılarımızda olduğu gibi, bu yazımızda da önce problemi anlamaya çalışalım dilerseniz.
Yalnız şimdi yazıya böyle ciddi başladım ya, nerede nasıl kıvırsam da olayı bir yerde goygoya bağlasam diye düşünüyorum. Sevgili okur, öyle bir yazı yazmak istiyorum ki; senin okurken aldığın keyfi ben de yazarken alayım. Hah şöyle işte ya… Neyse, rahatladım sonuçta. Ehem… Nerede kalmıştık. Hah! Önce problemi anlayalım diyordum.
Biliyorsunuz ki, bir uygulamanın nesne modelini geliştirirken inheritance kavramından oldukça faydalanıyoruz. Önce, genel (base) sınıfımızı oluşturup sonra da özel (derived) sınıflarımızı buradan türetiyoruz. Böylelikle bir hiyerarşi elde ediyoruz. Ancak bu yukarıdan aşağıya doğru hareket eden bu hiyerarşi, eğer çok fazla dallanırsa geliştirilebilirliği kısıtlayabiliyor.
Sözün tam da bu noktasında sanırım pratik bir örnek vermek gerek (bakın nasıl da biliyorum 🙂 ). Hatta isterseniz bu makalede biraz daha farklı bir şey yapalım. Doğrudan senaryo üzerinden gidelim ve problemin ne olduğuna bakalım. Sonra da Bridge Design Pattern’e göre refactoring yaparız olmaz mı? Bence süper olur.
O zaman senaryomuzu düşünelim… Hmm… Uygulamamız belirli bir şablonda rapor hazırlayan (Satış raporu, çalışan performans raporu gibi) ve bu raporu da farklı formatlarda (Web Formatı ya da masaüstü formatı gibi) sisteme kaydeden bir işleve sahip olsun. Böyle bir durumda aşağıdaki gibi bir nesne modeli oluşturabiliriz sanırım.
İki rapor türü ve iki rapor formatı için fıstık gibi bir model. Doğruya doğru. Mamafih, geliştirilebilir bir mimari mi? Yani doğrudan şunu soruyorum. Başka bir formatta (diyelim ki Mobile) rapor üretmeniz gerekirse, her rapor türü için ayrı ayrı implementasyon yapmanız gerekiyor değil mi? Peki bu ne demek? İstemcinin kodunda değişiklik yapmak zorunda kalmak demek… E O zaman SOLID prensiplerinin O’su (Open Closed Principle) yalan olmaz mı? Olur (nasıl da her soruya kendim cevap veriyorum ama ehe :)).
Peki, nasıl çözeceğiz bu problemi? E o da sorumu canım… Makalemizin konusu o zaten: Bridge Pattern işte bu durumları çözmek için var (bu reklamcı ağızıyla kristal elmanın sahibi ben olmalıyım işte).
Bridge Design Pattern diyor ki; soyutlaşmış (abstract) bir yapıyı, implementasyondan ayır. Böylece bağımsız olarak geliştirilebilir iki yapı elde edersin.
Gelin bu cümleyi yukarıdaki model üzerinde düşünerek iyice bir anlayalım. İmplementasyondan kastettiği en sonda elde edeceğiniz sınıf olacaktır. Diyelim ki bu sınıf, DesktopSalesReport isimli sınıf olsun. Gelelim soyutlaşmış yapıyı belirleme kısmına… Hemen şunu sorarak zihnimizi berraklaştıralım; yukarıdaki modelde WebEmpPerformanceReport ile DesktopSalesReport sınıfları ortak bir atadan türediklerine göre birbirleriyle akrabalar öyle değil mi? Bakın tam bu noktadaki kırılmayı görebiliyor musunuz? Belirttiğim iki sınıf geliştirilebilir bir modelde olabilmesi için akraba olmamalılar. Nitekim Web formatında oluşturulmuş çalışan performans raporu başka bir şey, masaüstü formatında oluşturulmuş satış raporu ise bambaşka.
O zaman şöyle bir tanım yapabilir miyiz? Bu iki sınıf da farklı iki formatta bir rapordur. O halde bakın ne açığa çıktı! Rapor Formatı, bu modelde soyutlaşmış bir yapıdır. Yani, satış ya da çalışan performans raporunu oluştururken, hangi formatta (Masaüstü veya web) kaydetmesi gerektiğini söylemem yeterli olacak. Bu durumu ayarlamak için IReportFormat isminde bir interface oluşturuyorum ve ilgili formatları bu interface’den implemente ediyorum:
Bakın böylece, geliştirilebilir iki yapım var artık! Formatları kendi yapısı içinde çoğaltabileceğim gibi, raporları da çoğaltabilirim. Hadi o zaman kodlara bakalım.
Buraya kadar ayırdığımız abstract yapının kodları… Şimdi de diğer kısım. İlk olarak Report sınıfına dikkat!
Report sınıfına ve ondan türeyen sınıflara dikkat ettiniz mi? Varsayılan Constructor üyeleri, IReportFormat interface’ini parametre olarak alıyorlar ve bunu ReportFormat özelliğine set ediyorlar. Böylelikle ben, dilediğim formatta ve kategoride rapor üretebiliyorum. Hadi client üzerindeki kodu da yazalım da bitsin bu iş.
Şimdi bu sayede; istemcide (yukarıdaki Main() metodu) doğrudan herhangi bir değişiklik yapmadan geliştirme yapabileceğim artık. İşte Bridge bu yüzden harika bir şey!
Bu arada fark etmenizi istediğim bir şey daha var. .NET içinde birçok sınıf bu pattern’ i kullanıyor… Hadi gelin bunu bir tartışma konusu haline getirelim.
Sizce, hangi .NET sınıfları bu deseni kullanıyor?
Haydi bakalım… Bir sonraki yazıda görüşmek üzere…
elinize saglik, cok guzel ve yalin bir anlatim.
Teşekkürler 🙂 Fakat hangi sınıf bridge oluyor?
Selam. Aslında, bir Bridge sınıfı yok. Bu desene “köprü” isminin verilmesinin sebebi, iki nesne arasında bir özellik aracılığı ile bağlantı kurması. Yani, bizim örneğimizde, “Rapor” nesnesi ile “RaporFormatı” nesnesi arasında özellik üzerinden bir bağlantı kuruluyor. Umarım yanıt verebilmişimdir 🙂 Aklınıza yatmazsa da yorumunuzu bekliyorum.
Elinize sağlık. Güzel, anlaşılır bir yazı olmuş :))
string sınıfı Format metodunu kullanırken (string.format(decimal),string.format(date) vs.) bridge kokusu alıyorum, doğru mu?