Bridge Design Pattern
Bridge Design Pattern
The Bridge pattern is a structural design pattern that decouples an abstraction from its implementation so that the two can vary independently. This pattern is useful when we need to create a system that can support multiple platforms or devices.
The Bridge pattern consists of two parts:
- Abstraction: The Abstraction defines the interface for the "control" part of the system. It maintains a reference to an Implementor object and delegates the implementation to it.
- Implementor: The Implementor defines the interface for the "platform" part of the system. It provides concrete implementations for the operations defined by the Abstraction.
The Bridge pattern lets you build a system that supports multiple platforms or devices by separating the "control" part from the "platform" part. This separation means you can change the implementation on either side without affecting the other.
The Problem
Suppose you are developing a drawing application that renders different shapes on different platforms, such as Windows and Linux. You want to support multiple platforms and shapes without creating a separate class for every combination.
The Solution
The Bridge pattern solves this by decoupling the abstraction (the "control" part) from its implementation (the "platform" part). By defining separate hierarchies for each, you can support any combination of platforms and shapes without class explosion.
Real World Example
This example builds a drawing application that renders different shapes on different platforms. It defines an
abstraction (the Shape class) and an implementation interface (the Renderer interface), then creates concrete
implementations of each — Circle and Square for shapes, WindowsRenderer and LinuxRenderer for platforms.
<?php
// Implementor interface
interface Renderer {
public function renderCircle();
public function renderSquare();
}
// Concrete Implementor: WindowsRenderer
class WindowsRenderer implements Renderer {
public function renderCircle() {
echo "Rendering circle on Windows platform\n";
}
public function renderSquare() {
echo "Rendering square on Windows platform\n";
}
}
// Concrete Implementor: LinuxRenderer
class LinuxRenderer implements Renderer {
public function renderCircle() {
echo "Rendering circle on Linux platform\n";
}
public function renderSquare() {
echo "Rendering square on Linux platform\n";
}
}
// Abstraction: Shape
abstract class Shape {
protected $renderer;
public function __construct(Renderer $renderer) {
$this->renderer = $renderer;
}
abstract public function draw();
}
// Concrete Abstraction: Circle
class Circle extends Shape {
public function draw() {
$this->renderer->renderCircle();
}
}
// Concrete Abstraction: Square
class Square extends Shape {
public function draw() {
$this->renderer->renderSquare();
}
}
// Usage
$windowsRenderer = new WindowsRenderer();
$linuxRenderer = new LinuxRenderer();
$circle = new Circle($windowsRenderer);
$circle->draw(); // Output: Rendering circle on Windows platform
$square = new Square($linuxRenderer);
$square->draw(); // Output: Rendering square on Linux platform
The Shape abstraction delegates all rendering work to the Renderer implementation it holds. Circle calls
renderCircle() and Square calls renderSquare(). Because the two hierarchies are independent, you can add a new
platform by writing a single Renderer implementation — no changes to Shape, Circle, or Square are needed.
Benefits of the Bridge Pattern
The Bridge pattern offers several benefits, including:
- Separation of concerns: The abstraction and the implementation vary independently, each in its own hierarchy.
- Encapsulation: Implementation details stay in their own classes, making the code easier to maintain and extend.
- Flexibility: You can change either side of the bridge without touching the other.
- Reusability: Different abstractions can share the same implementation, reducing duplication.
- Testability: The abstraction and implementation can be tested in isolation, improving overall test coverage.
Together, these benefits make the Bridge pattern a strong choice whenever your system needs to support multiple platforms or devices.
Conclusion
The Bridge pattern decouples an abstraction from its implementation, allowing each to evolve independently. If you need to build a system that spans multiple platforms or devices, the Bridge pattern gives you a modular and extensible way to do so without resorting to a combinatorial explosion of classes.