Bridge Pattern
Bridge Pattern
Decouple an abstraction from its implementation so that the two can vary independently.
Intent
The Bridge pattern uses composition instead of inheritance to separate two class hierarchies: the abstraction hierarchy (e.g., Shape) and the implementation hierarchy (e.g., Renderer). Without Bridge, each combination of shape and renderer requires its own subclass — 3 shapes × 4 renderers = 12 concrete classes. With Bridge, the abstraction holds a reference to the implementor interface, reducing that to 3 + 4 = 7 classes.
This is a prospective design decision — made upfront when designing a system that needs both hierarchies to vary independently. This distinguishes Bridge from Adapter, which is a retrospective fix applied to two pre-existing incompatible interfaces.
When NOT to Use
- Only one implementation exists and no variation is expected — a direct implementation is simpler
- The abstraction and implementation are stable and unlikely to vary independently — Bridge adds indirection for no benefit
- You are reconciling existing incompatible interfaces — use Adapter instead
When to Use
- Both abstraction and implementation need to be extensible via subclassing without a class explosion (e.g., 3 shapes × 4 renderers = 12 classes without Bridge, 7 with Bridge)
- Implementation details should be hidden from the client and replaceable at runtime
How It Works
Participants: Abstraction (holds a reference to an Implementor; defines the high-level interface), RefinedAbstraction (extends Abstraction with additional behaviour), Implementor (interface defining low-level operations), ConcreteImplementor (implements Implementor).
The Abstraction delegates implementation work to its Implementor rather than implementing it directly. The client interacts with the Abstraction interface; the Implementor used underneath can be swapped at construction time or at runtime.
Class Diagram
classDiagram
class Abstraction {
#impl : Implementor
+operation()
}
class RefinedAbstraction {
+operation()
}
class Implementor {
<<interface>>
+operationImpl()*
}
class ConcreteImplementorA {
+operationImpl()
}
class ConcreteImplementorB {
+operationImpl()
}
Abstraction <|-- RefinedAbstraction
Abstraction o-- Implementor : bridge (composition)
Implementor <|.. ConcreteImplementorA
Implementor <|.. ConcreteImplementorB
TypeScript Example
// Bridge — TypeScript
// Source: GoF Design Patterns, p.151
interface Renderer {
renderShape(shape: string): void;
}
class SVGRenderer implements Renderer {
renderShape(shape: string) { console.log(`SVG: ${shape}`); }
}
abstract class Shape {
constructor(protected renderer: Renderer) {}
abstract draw(): void;
}
class Circle extends Shape {
draw() { this.renderer.renderShape("circle"); }
}Java Example
// Bridge — Java
// Source: GoF Design Patterns, p.151
interface Renderer { void renderShape(String shape); }
class SVGRenderer implements Renderer {
public void renderShape(String shape) { System.out.println("SVG: " + shape); }
}
abstract class Shape {
protected Renderer renderer;
Shape(Renderer r) { this.renderer = r; }
abstract void draw();
}
class Circle extends Shape {
Circle(Renderer r) { super(r); }
@Override public void draw() { renderer.renderShape("circle"); }
}Related Concepts
| Pattern | Relationship |
|---|---|
| Adapter-Pattern | Adapter reconciles two existing incompatible interfaces (retrospective); Bridge separates a freshly-designed abstraction from its implementation (prospective) |
| Strategy-Pattern | Strategy swaps a single algorithm at runtime; Bridge separates entire class hierarchies. Strategy is a behavioral pattern; Bridge is structural (created Phase 9) |
| Composite-Pattern | Both use recursive composition; Composite focuses on part-whole tree structures |
Sources
- Gamma, Helm, Johnson, Vlissides — Design Patterns, Addison-Wesley, 1994, p.151