Structural — Yapısal Desenler
Bridge
Köprü Deseni
Amaç: Bir soyutlamayı (abstraction) uygulamasından (implementation) ayırarak, ikisinin birbirinden bağımsız şekilde geliştirilebilmesini sağlamak.
Basit Anlatım
Teknik terim yok — günlük hayattan bir benzetme
Evinizde bir televizyon ve bir de klima var. İkisi için de kumanda kullanıyorsunuz. Basit kumanda yalnızca aç/kapa ve ses/sıcaklık tuşlarına sahip. Gelişmiş kumandada ise ek olarak sessize alma, zamanlayıcı gibi tuşlar var.
Kumanda türleri (basit, gelişmiş) ile cihaz türleri (TV, klima, radyo) iki ayrı dünyadır. Her kombinasyon için ayrı bir ürün üretseydik: "TV için basit kumanda", "TV için gelişmiş kumanda", "klima için basit kumanda"… 3 cihaz × 2 kumanda = 6 farklı ürün. Yeni bir cihaz geldiğinde 2 ürün daha!
Bunun yerine her kumanda, cihazla ortak bir "dil" (kızılötesi sinyal) üzerinden konuşur. Herhangi bir kumanda herhangi bir cihazla eşleşebilir. Aradaki o sinyal bağlantısı bir köprüdür.
Teknik Anlamı
Bir yazılımcının bakış açısıyla
Bridge, kalıtım yerine composition kullanarak sınıf hiyerarşisindeki kombinasyon patlamasını önler. Bir hiyerarşi (Abstraction) diğerine (Implementor) bir referans tutar ve işi ona delege eder.
Problem: Kartezyen çarpım
Shape → Circle, Square ve renkler Red, Blue olsun. Kalıtımla RedCircle, BlueCircle, RedSquare, BlueSquare yazarsınız. Her yeni şekil veya renk sınıf sayısını çarparak artırır. Bridge ile N + M sınıf yeterlidir, N × M değil.
Katılımcılar
- Abstraction: Üst seviye kontrol mantığı; Implementor'a referans tutar (
Notification). - RefinedAbstraction: Abstraction'ın genişletilmiş halleri (
UrgentNotification). - Implementor: Alt seviye işlemlerin arayüzü (
IMessageSender). - ConcreteImplementor: Platforma özgü uygulamalar (
EmailSender,SmsSender).
Adapter'dan farkı
Adapter, var olan uyumsuz sınıfları sonradan uyumlu hale getirir. Bridge ise baştan tasarlanır: iki boyutun bağımsız büyüyeceği öngörülür.
Avantajlar
- Soyutlama ve uygulama bağımsız olarak genişletilebilir (Open/Closed).
- Uygulama çalışma zamanında değiştirilebilir.
- İstemci, platforma özgü detaylardan yalıtılır.
C# ile Uygulama
Adım adım, çalışan kod örneği
Senaryo: Bir bildirim sistemi. Bildirim türleri (normal, acil) ve gönderim kanalları (e-posta, SMS, Slack) bağımsız olarak çoğalabilmeli.
Adım 1 Implementor: gönderim kanalı arayüzü ve uygulamaları
public interface IMessageSender
{
void Send(string title, string body);
}
public class EmailSender : IMessageSender
{
public void Send(string title, string body) =>
Console.WriteLine($" [E-posta] Konu: {title} | {body}");
}
public class SmsSender : IMessageSender
{
public void Send(string title, string body) =>
Console.WriteLine($" [SMS] {title}: {body}");
}
public class SlackSender : IMessageSender
{
public void Send(string title, string body) =>
Console.WriteLine($" [Slack #genel] *{title}* {body}");
}Adım 2 Abstraction: bildirim hiyerarşisi
Notification, gönderim kanalını bir alan olarak tutar (köprü budur). Alt sınıflar mesajın nasıl biçimleneceğini belirler, nasıl gönderileceğini değil.
public abstract class Notification
{
protected readonly IMessageSender Sender; // <-- köprü
protected Notification(IMessageSender sender) => Sender = sender;
public abstract void Notify(string message);
}
public class NormalNotification : Notification
{
public NormalNotification(IMessageSender sender) : base(sender) { }
public override void Notify(string message) => Sender.Send("Bilgi", message);
}
public class UrgentNotification : Notification
{
public UrgentNotification(IMessageSender sender) : base(sender) { }
public override void Notify(string message)
{
// Acil bildirim 2 kez ve büyük harfle gönderilir.
for (int i = 1; i <= 2; i++)
Sender.Send($"ACİL ({i}/2)", message.ToUpper());
}
}Adım 3 Kullanım
Tüm parçaları bir araya getiren istemci (client) kodu:
Console.WriteLine("Normal + E-posta:");
new NormalNotification(new EmailSender()).Notify("Haftalık rapor hazır.");
Console.WriteLine("Acil + SMS:");
new UrgentNotification(new SmsSender()).Notify("Sunucu yanıt vermiyor");
Console.WriteLine("Acil + Slack:");
new UrgentNotification(new SlackSender()).Notify("Disk doldu");Konsol çıktısı
Normal + E-posta: [E-posta] Konu: Bilgi | Haftalık rapor hazır. Acil + SMS: [SMS] ACİL (1/2): SUNUCU YANIT VERMİYOR [SMS] ACİL (2/2): SUNUCU YANIT VERMİYOR Acil + Slack: [Slack #genel] *ACİL (1/2)* DİSK DOLDU [Slack #genel] *ACİL (2/2)* DİSK DOLDU
Kodda dikkat edilmesi gerekenler
- 2 bildirim türü × 3 kanal = 6 kombinasyon, ama yalnızca 2 + 3 = 5 sınıf yazdık.
- "WhatsApp" kanalı eklemek: tek bir
WhatsAppSendersınıfı. "Zamanlanmış bildirim" eklemek: tek birNotificationalt sınıfı. Birbirlerini etkilemezler.