Bridge Pattern_
// "Simplicity is the soul of efficiency" - Austin Freeman
call_split What does it consist of?
The Bridge is a structural pattern that divides a large class or a closely related group of classes into two separate hierarchies: abstraction and implementation, which can be developed independently.
This pattern is especially useful when we have a class with multiple dimensions of variation. Instead of creating a subclass for every possible combination (which leads to an exponential explosion of classes), we separate those dimensions.
Practical Example: Remote Controls and Devices
Imagine we have a RemoteControl
class and a Device
class. A remote control can be basic or advanced, and a device can be a TV or a Radio. If we mix
everything, we would have BasicTVRemote,
AdvancedTVRemote,
BasicRadioRemote,
and so on.
# The logic behind it
The Bridge pattern solves this by extracting one of the dimensions into a separate hierarchy. The RemoteControl
(Abstraction) contains a reference to an object of type Device
(Implementation).
The remote delegates the actual work (like turning on or changing the volume) to the linked device object. This allows us to change the devices and the remotes completely independently from one another.
# Structure
# Implementation
<?php
// 1. Implementation
interface Device {
public function isEnabled(): bool;
public function enable(): void;
public function disable(): void;
public function getVolume(): int;
public function setVolume(int $percent): void;
}
// 2. Concrete Implementations
class TV implements Device {
private bool $on = false;
private int $volume = 30;
public function isEnabled(): bool { return $this->on; }
public function enable(): void { $this->on = true; }
public function disable(): void { $this->on = false; }
public function getVolume(): int { return $this->volume; }
public function setVolume(int $percent): void { $this->volume = $percent; }
}
class Radio implements Device {
private bool $on = false;
private int $volume = 10;
public function isEnabled(): bool { return $this->on; }
public function enable(): void { $this->on = true; }
public function disable(): void { $this->on = false; }
public function getVolume(): int { return $this->volume; }
public function setVolume(int $percent): void { $this->volume = $percent; }
}
// 3. Abstraction
class RemoteControl {
protected Device $device;
public function __construct(Device $device) {
$this->device = $device;
}
public function togglePower(): void {
echo "Remote: Power toggle\n";
if ($this->device->isEnabled()) {
$this->device->disable();
} else {
$this->device->enable();
}
}
public function volumeDown(): void {
echo "Remote: Volume down\n";
$this->device->setVolume($this->device->getVolume() - 10);
}
}
// 4. Refined Abstraction
class AdvancedRemoteControl extends RemoteControl {
public function mute(): void {
echo "Remote: Mute\n";
$this->device->setVolume(0);
}
}
// Usage
$tv = new TV();
$remote = new RemoteControl($tv);
$remote->togglePower();
$radio = new Radio();
$advancedRemote = new AdvancedRemoteControl($radio);
$advancedRemote->togglePower();
$advancedRemote->mute();
# Mapping Participants
-
Device (Implementation): Declares the interface common to all concrete implementations.
-
TV, Radio (ConcreteImplementations): The concrete components that implement the
Deviceinterface. -
RemoteControl (Abstraction): High-level control logic. It delegates work to the concrete implementation.
-
AdvancedRemoteControl (RefinedAbstraction): Extends the control logic of the basic abstraction.
# Conclusions
The Bridge pattern prevents class explosion when extending logic in multiple dimensions independently. In our example, we can add new devices (like a Projector) without modifying existing remotes, and we can add new remotes (like a SmartAppRemote) without changing existing devices.
# Related Patterns
Adapter
Bridge is typically designed upfront so that abstractions and implementations can vary independently. Adapter is applied after the fact to make unrelated classes work together.
State / Strategy
Bridge, State, Strategy (and to some extent Adapter) have very similar structures as all are based on composition. However, they solve different problems.
Abstract Factory
Abstract Factory can be paired with Bridge. This is useful when some abstractions defined by Bridge can only work with specific implementations.