Structural · two axes, not a wrap of a vendor
Bridge
A is a structural pattern that separates two independent hierarchies so each can grow without creating a new class for every combination. One hierarchy is the high-level API; the other is the platform or mechanism it talks to.
A concrete failure mode: a drawing app starts with Circle and Square, then red and blue, then “draw on screen” and “print on a plotter.” CircleRedScreen, CircleRedPlotter, SquareBlueScreen… the class list becomes a multiplication table. That mess is a . Bridge is the fix.
The multiplication table you should refuse to write
Remote features × device types. Every cell is a class. Add one device, and you double the rows. Use Bridge when you can already see this table — not merely because “I wrapped something.”
| Basic | Advanced | |
|---|---|---|
| Tv | BasicTvRemote | AdvancedTvRemote |
| Radio | BasicRadioRemote | AdvancedRadioRemote |
| Stick | BasicStickRemote | AdvancedStickRemote |
6 classes for 2 × 3. The next feature makes it 8. Stop and split the axes.
Split into N + M, not N × M
— how we talk
- Remote — togglePower
- AdvancedRemote — mute
— what we talk to
- Tv — turnOn, setVolume
- Radio — turnOn, setVolume
Remote holds a Device. AdvancedRemote.mute() calls device.setVolume(0). You never write AdvancedTvRemote. Name the first. If you cannot, you do not need Bridge.
Read the diagram definition
flowchart LR
subgraph abs [Abstraction]
R[Remote]
B[BasicRemote]
A[AdvancedRemote]
B --> R
A --> R
end
subgraph impl [Implementor]
D[Device]
T[Tv]
Radio[Radio]
T --> D
Radio --> D
end
R -->|holds| DCore question
Do I have two independent axes of change that will explode if I subclass both?
Java sketch: Remote × device, not RemoteTv subclasses
interface Device {
void turnOn();
void turnOff();
void setVolume(int level);
}
class Tv implements Device { /* hardware calls */ }
class Radio implements Device { /* hardware calls */ }
class Remote {
protected final Device device;
Remote(Device device) { this.device = device; }
void togglePower() { device.turnOn(); }
}
class AdvancedRemote extends Remote {
AdvancedRemote(Device device) { super(device); }
void mute() { device.setVolume(0); }
}
// new AdvancedRemote(new Tv()) — not AdvancedTvRemoteShape × color — the same split
Circle does not subclass into CircleRed. Circle holds a Color. A new shape is one class. A new paint is one class. You do not need a class per pair.
interface Color {
String hex();
}
class Red implements Color {
public String hex() { return "#FF0000"; }
}
abstract class Shape {
protected final Color color;
Shape(Color color) { this.color = color; }
abstract void draw();
}
class Circle extends Shape {
Circle(Color color) { super(color); }
void draw() { /* stroke a circle in color.hex() */ }
}Machine-coding cousin: Document × Store
“Remote vs local” in interviews is the same move. A high-level operation holds a LocalStore or a RemoteStore. The save/load code does not fork. Cache, Queue, and Notification (severity × channel) are the same two-axis picture.
interface Store {
void put(String id, byte[] body);
byte[] get(String id);
}
class LocalDiskStore implements Store { /* files */ }
class RemoteHttpStore implements Store { /* network */ }
class Document {
private final Store store;
Document(Store store) { this.store = store; }
void save(String id, byte[] body) { store.put(id, body); }
}
class VersionedDocument extends Document {
VersionedDocument(Store store) { super(store); }
void saveVersioned(String id, byte[] body, int rev) {
save(id + "@" + rev, body);
}
}Bridge is not Adapter, Strategy, or a random wrap
vs Adapter
Adapter appears when a class already exists and will not match your interface. Bridge appears when you plan two hierarchies. Same word “wrap,” different timing and different problem.
vs Strategy
Strategy varies one algorithm (pricing, allocation) under a single context. Bridge varies a whole implementation hierarchy under a whole abstraction hierarchy. If only one axis moves, Strategy is enough.
vs Facade
Facade hides many classes behind one method. Bridge does not hide a subsystem; it decouples two dimensions so they can grow. Clients still see Remote and Device.
How to implement it (four steps)
- 01
Name the two axes
Shape vs color. Remote vs device. Document vs storage. If you cannot name two, stop.
- 02
Put one axis behind an interface
Device, DrawingApi, Color, Store — the part the other axis will call.
- 03
Hold it in the abstraction
Remote has a Device. Inject it. Do not new Tv inside every Remote subclass.
- 04
Grow each side alone
Add AdvancedRemote without touching Tv. Add Radio without touching Remote.
Common mistakes
Calling Adapter a Bridge
A Bridge for a single axis
new Tv() inside BasicRemote
How to explain this in an interview
“I see two axes — remote features and device types — so I will Bridge: Remote holds Device. I will not subclass AdvancedTvRemote. This is not an adapter; both hierarchies are ours.”
- Count classes for 3 remotes × 4 devices without Bridge, then with Bridge.
- Notification severity × Email/SMS/Push. Is that Bridge, Strategy, or both? Defend the split.
Takeaway
Use Bridge when you can already see a multiplication table of subclasses. Split the axes, inject one into the other, and grow each side alone. Do not use Bridge as a fancy name for Adapter.