Bridge (Brygga)
Introduktion
Bridge är ett strukturmönster som delar upp en stor klass — eller en familj av nära relaterade klasser — i två separata hierarkier: abstraktion och implementation. De kan sedan utvecklas oberoende av varandra.
Problem
Du har en Shape-klass med underklasserna Circle och Square. Du vill lägga till färger — Red och Blue. Med arv får du RedCircle, BlueCircle, RedSquare, BlueSquare — fyra klasser för två former och två färger. Lägger du till en tredje färg behöver du ytterligare två klasser. Kombinationerna exploderar.
Lösning
Bridge extraherar en av dimensionerna till en separat hierarki. Formerna håller en referens till ett färgobjekt istället för att ärva det. Form och färg kan nu utökas helt oberoende.
När ska Bridge användas?
- När du vill undvika en permanent bindning mellan abstraktion och implementation.
- När både abstraktioner och implementationer ska kunna utökas via arv oberoende.
- När ändringar i implementationen inte ska påverka klientkoden.
Struktur
| Roll | Ansvar |
|---|---|
| Abstraction | Håller en referens till Implementation; definierar det högnivågränssnitt klienten använder. |
| Refined Abstraction | Utökar Abstraction med extra logik. |
| Implementation Interface | Definierar gränssnittet för implementationsklasserna. |
| Concrete Implementation | Specifik implementation av Implementation-gränssnittet. |
Exempel — Enhetsnotifieringar (Java)
Scenariot: Notifieringar (BasicNotification, UrgentNotification) ska kunna skickas via olika kanaler (EmailSender, SmsSender) — helt oberoende av varandra.
// ── Implementation Interface ──────────────────────────────
public interface MessageSender {
void sendMessage(String recipient, String subject, String body);
}
// ── Concrete Implementations ──────────────────────────────
public class EmailSender implements MessageSender {
@Override
public void sendMessage(String recipient, String subject, String body) {
System.out.println("📧 E-post till " + recipient);
System.out.println(" Ämne: " + subject);
System.out.println(" Meddelande: " + body);
}
}
public class SmsSender implements MessageSender {
@Override
public void sendMessage(String recipient, String subject, String body) {
System.out.println("📱 SMS till " + recipient + ": " + subject + " — " + body);
}
}
public class SlackSender implements MessageSender {
private String workspace;
public SlackSender(String workspace) {
this.workspace = workspace;
}
@Override
public void sendMessage(String recipient, String subject, String body) {
System.out.println("💬 Slack [" + workspace + "] → " + recipient + ": " + subject);
}
}
// ── Abstraction ───────────────────────────────────────────
public abstract class Notification {
protected MessageSender sender;
public Notification(MessageSender sender) {
this.sender = sender;
}
public abstract void notify(String recipient, String event);
}
// ── Refined Abstractions ──────────────────────────────────
public class BasicNotification extends Notification {
public BasicNotification(MessageSender sender) {
super(sender);
}
@Override
public void notify(String recipient, String event) {
sender.sendMessage(recipient, "Notifiering", "Händelse inträffade: " + event);
}
}
public class UrgentNotification extends Notification {
public UrgentNotification(MessageSender sender) {
super(sender);
}
@Override
public void notify(String recipient, String event) {
sender.sendMessage(
recipient,
"🚨 BRÅDSKANDE: " + event,
"Kräver omedelbar åtgärd! Händelse: " + event
);
}
}
public class DigestNotification extends Notification {
private java.util.List<String> events = new java.util.ArrayList<>();
public DigestNotification(MessageSender sender) {
super(sender);
}
public void addEvent(String event) {
events.add(event);
}
@Override
public void notify(String recipient, String title) {
String body = String.join(", ", events);
sender.sendMessage(recipient, "Sammanfattning: " + title, body);
events.clear();
}
}
// ── Klientkod ─────────────────────────────────────────────
public class Application {
public static void main(String[] args) {
// Kombinera abstraktioner med implementationer fritt
Notification emailAlert = new UrgentNotification(new EmailSender());
emailAlert.notify("ops@bytebase.se", "Databasen svarar inte");
System.out.println();
Notification smsBasic = new BasicNotification(new SmsSender());
smsBasic.notify("+46701234567", "Schemalagt jobb slutfört");
System.out.println();
DigestNotification digest = new DigestNotification(new SlackSender("bytebase"));
digest.addEvent("Användare registrerad");
digest.addEvent("Order skapad");
digest.addEvent("Betalning mottagen");
digest.notify("#daglig-rapport", "Dagens händelser");
}
}
Output:
📧 E-post till ops@bytebase.se
Ämne: 🚨 BRÅDSKANDE: Databasen svarar inte
Meddelande: Kräver omedelbar åtgärd! Händelse: Databasen svarar inte
📱 SMS till +46701234567: Notifiering — Händelse inträffade: Schemalagt jobb slutfört
💬 Slack [bytebase] → #daglig-rapport: Sammanfattning: Dagens händelser
Fördelar
- Open/Closed Principle — Lägg till nya abstraktioner och implementationer oberoende av varandra.
- Undviker kombinatorisk explosion av underklasser.
- Klientkoden arbetar mot abstraktionen och behöver aldrig känna till implementationen.
Nackdelar
- Ökar komplexiteten — ytterligare ett lager av inriktning kan göra enkel kod svårare att följa.
Av Victor Hernandez från Bytebase.se