Bridge — separating abstraction from implementation
Concept
Bridge pattern separates an abstraction from its implementation so that the two can vary independently. Instead of a single inheritance hierarchy doing double duty (abstracting AND implementing), Bridge splits them into two separate hierarchies connected by composition.
The problem without Bridge: You have Shape (Circle, Square) and rendering backends (OpenGL, DirectX, SVG). Combining them with inheritance creates: OpenGLCircle, DirectXCircle, SVGCircle, OpenGLSquare, DirectXSquare, SVGSquare — 6 classes for 3×2. Add a new shape and renderer, and the count explodes. Bridge solves this with composition: separate the shape hierarchy from the renderer hierarchy, connect with a reference.
Structure:
- Abstraction (
Shape): The high-level control. Has a reference toImplementorInterface. - Refined Abstraction (
Circle,Square): Extends Abstraction. - Implementor Interface (
RendererInterface): Implementation interface (distinct from Abstraction's interface). - Concrete Implementor (
OpenGLRenderer,SVGRenderer): Concrete implementations.
Key insight: The "bridge" is the reference from Abstraction to Implementor. Abstractions delegate to Implementors. Two independent hierarchies, coupled through an interface.
Bridge vs Strategy: Both use composition and an interface. Bridge is about splitting a class that does two independent things into separate hierarchies. Strategy is about making one behavior swappable. Bridge is a structural pattern (about structure); Strategy is behavioral (about behavior at runtime).
Real-world: Database abstraction layers (PDO bridges PHP code to MySQL, PostgreSQL, SQLite). UI toolkits. Notification systems (notification type × delivery channel).
Code Example
<?php
// Implementor hierarchy — rendering backends
interface RendererInterface
{
public function renderCircle(float $x, float $y, float $radius): string;
public function renderSquare(float $x, float $y, float $side): string;
}
class SVGRenderer implements RendererInterface
{
public function renderCircle(float $x, float $y, float $radius): string
{
return "<circle cx='{$x}' cy='{$y}' r='{$radius}' />";
}
public function renderSquare(float $x, float $y, float $side): string
{
return "<rect x='{$x}' y='{$y}' width='{$side}' height='{$side}' />";
}
}
class CanvasRenderer implements RendererInterface
{
public function renderCircle(float $x, float $y, float $radius): string
{
return "ctx.arc({$x}, {$y}, {$radius}, 0, 2*Math.PI);";
}
public function renderSquare(float $x, float $y, float $side): string
{
return "ctx.fillRect({$x}, {$y}, {$side}, {$side});";
}
}
// Abstraction hierarchy — shapes
abstract class Shape
{
public function __construct(
protected float $x,
protected float $y,
protected readonly RendererInterface $renderer, // THE BRIDGE
) {}
abstract public function render(): string;
abstract public function area(): float;
}
class Circle extends Shape
{
public function __construct(
float $x, float $y,
private readonly float $radius,
RendererInterface $renderer,
) {
parent::__construct($x, $y, $renderer);
}
public function render(): string { return $this->renderer->renderCircle($this->x, $this->y, $this->radius); }
public function area(): float { return M_PI * $this->radius ** 2; }
}
class Square extends Shape
{
public function __construct(
float $x, float $y,
private readonly float $side,
RendererInterface $renderer,
) {
parent::__construct($x, $y, $renderer);
}
public function render(): string { return $this->renderer->renderSquare($this->x, $this->y, $this->side); }
public function area(): float { return $this->side ** 2; }
}
// Combine independently — 2 shapes × 2 renderers = 4 combinations, no new classes
$svgCircle = new Circle(50, 50, 30, new SVGRenderer());
$svgSquare = new Square(10, 10, 40, new SVGRenderer());
$canvasCircle = new Circle(50, 50, 30, new CanvasRenderer());
echo $svgCircle->render(); // <circle cx='50' cy='50' r='30' />
echo $canvasCircle->render(); // ctx.arc(50, 50, 30, 0, 2*Math.PI);
// Add a new renderer (WebGL) without touching Shape classes
// Add a new shape (Triangle) without touching renderer classes