Bridge
Split a class into two hierarchies — abstraction and implementation — that can vary independently so you don't get a combinatorial explosion of subclasses.
Intent & Description
🎯 Intent
Decouple ‘what something does’ (abstraction) from ‘how it does it’ (implementation) so both can evolve independently.
📋 Context
You have a Shape class and want to support different rendering APIs (OpenGL, Vulkan, Canvas). Or a RemoteControl that works with different Device types. Combining them in a single hierarchy gives you N×M subclasses (CircleOpenGL, CircleVulkan, SquareOpenGL…). It doesn’t scale.
💡 Solution
Separate into two hierarchies. The Abstraction (RemoteControl) holds a reference to an Implementor interface (Device). Refined Abstractions extend RemoteControl; Concrete Implementations implement Device. Mix and match any combination — no extra subclasses needed.
Real-world Use Case
Source
📌 TL;DR
Two separate hierarchies connected by a reference. Abstraction uses the Implementor interface. N+M subclasses instead of N×M. Pointless with one implementation, essential when both dimensions grow.
Advantages
- Extend abstractions and implementations independently — N+M classes instead of N×M.
- Swap implementations at runtime by changing the reference.
Disadvantages
- Adds indirection that feels over-engineered when there’s only one implementation.
// Implementation
class Device {
isEnabled() {}
enable() {}
disable() {}
}
class Radio extends Device {
constructor() { super(); this.on = false; }
isEnabled() { return this.on; }
enable() { this.on = true; }
disable() { this.on = false; }
}
// Abstraction
class RemoteControl {
constructor(device) { this.device = device; }
togglePower() {
if (this.device.isEnabled()) {
this.device.disable();
} else {
this.device.enable();
}
}
}