What problem does the Bridge pattern solve, and how does it structure a solution in Java?
answer
- Two hierarchies: abstraction (what) + implementor (how)
- Abstraction HOLDS a reference to implementor = the bridge
- M×N classes collapse to M+N
- Composition over inheritance; designed up front
- Shape/DrawingApi or RemoteControl/Device example
basics
~20 sBridge splits one thing into two parts that can change separately: an abstraction (the 'what') and an implementation (the 'how'). The abstraction holds a reference to the implementation, so you can mix and match them instead of writing a class for every combination.
solid answer
~40 sBridge decouples an abstraction from its implementation so the two can vary independently. You define two separate interface hierarchies: an Abstraction (e.g. Shape) that exposes high-level operations, and an Implementor (e.g. DrawingApi) that exposes the low-level primitives. The Abstraction holds a reference to an Implementor and delegates the primitive work to it — that reference is the 'bridge'. Concrete abstractions (Circle, Square) extend the abstraction side; concrete implementors (VectorApi, RasterApi) extend the implementation side. Because they're wired by composition rather than inheritance, you add a new shape or a new rendering backend on its own axis without touching the other, and you avoid the M×N class explosion you'd get from subclassing every combination. In Java you typically wire the two together via the constructor.
code
java · 35 lines// Implementor — the 'how'
interface DrawingApi {
void drawCircle(double x, double y, double radius);
}
class VectorApi implements DrawingApi {
public void drawCircle(double x, double y, double r) {
System.out.printf("Vector circle at (%.0f,%.0f) r=%.0f%n", x, y, r);
}
}
class RasterApi implements DrawingApi {
public void drawCircle(double x, double y, double r) {
System.out.printf("Raster circle at (%.0f,%.0f) r=%.0f%n", x, y, r);
}
}
// Abstraction — the 'what'; holds the bridge to an Implementor
abstract class Shape {
protected final DrawingApi api; // the bridge
protected Shape(DrawingApi api) { this.api = api; }
abstract void draw();
}
class Circle extends Shape {
private final double x, y, r;
Circle(double x, double y, double r, DrawingApi api) {
super(api); this.x = x; this.y = y; this.r = r;
}
void draw() { api.drawCircle(x, y, r); } // delegate primitive work
}
// Client mixes any shape with any renderer at runtime
Shape s1 = new Circle(1, 2, 3, new VectorApi());
Shape s2 = new Circle(4, 5, 6, new RasterApi());
s1.draw(); s2.draw();go deeper
Can state that Bridge splits something into two parts that change independently and that one part holds a reference to the other.
Can name the four roles (abstraction, refined abstraction, implementor, concrete implementor), explain the M×N→M+N benefit, and sketch the Shape/DrawingApi code with constructor injection.
Articulates Bridge as 'prefer composition over inheritance' applied to two variation axes, explains when to reach for it (two dimensions foreseen up front), and distinguishes it cleanly from Strategy/Adapter.
Frames Bridge in terms of designing stable abstraction surfaces against swappable implementations, discusses trade-offs (added indirection, when NOT to use it), and connects it to broader layering/ports-and-adapters thinking.
## The problem Bridge solves Imagine you have a concept that varies along **two independent dimensions** at once. Classic example: a `Shape` (Circle, Square, Triangle…) that can be drawn with different rendering technologies (vector graphics, raster/pixel graphics). If you model every combination with inheritance you get `VectorCircle`, `RasterCircle`, `VectorSquare`, `RasterSquare`, … — with M shapes and N renderers you need **M×N classes**. Add one new shape and you must add N new classes; add one new renderer and you must add M. This is the **class explosion** problem, and inheritance forces it because a subclass binds *one* shape choice to *one* renderer choice permanently at compile time. ## The Bridge idea **Bridge** separates the two dimensions into two *independent* class hierarchies and connects them with a reference (the 'bridge') instead of inheritance: - The **Abstraction** — the high-level concept the client uses (`Shape`). It defines operations in terms of the client's vocabulary (`resizeBy`, `draw`). - The **Implementor** (sometimes called the *implementation interface*) — the low-level primitive operations (`DrawingApi` with `drawLine`, `drawCircle`). Note: 'implementor' here is a role name, NOT Java's `implements` keyword. - A **RefinedAbstraction** — concrete subclasses of the abstraction (`Circle`, `Square`). - A **ConcreteImplementor** — concrete classes implementing the implementor (`VectorApi`, `RasterApi`). The abstraction **holds a reference** to an implementor and **delegates** the primitive work to it. That held reference is literally the 'bridge' between the two hierarchies. ## Why this removes the explosion Now shapes vary along one axis and renderers along the other. You can pair *any* shape with *any* renderer at runtime by passing the renderer into the shape. M shapes + N renderers = **M + N classes**, not M×N. Adding a renderer means writing one class; every existing shape can use it immediately. ## How it looks in Java You define two interfaces (or an abstract class for the abstraction side), pass the implementor into the abstraction's constructor (constructor injection), and have the abstraction's methods call the implementor's methods. The client picks both halves and composes them. ## Key terms defined - **Abstraction**: the interface clients program against; the 'what'. - **Implementation/Implementor**: the interface of primitive operations; the 'how'. - **Delegation**: an object forwarding a call to another object it holds, rather than doing the work itself. - **Composition over inheritance**: building behavior by holding a reference to a collaborator instead of subclassing — the principle Bridge embodies. Bridge is a **structural** pattern (it's about how classes/objects are composed) and it's almost always **designed up front**, when you can already foresee two axes of variation.
- Why does Bridge reduce M×N combinations to M+N?Because the two axes of variation live in separate hierarchies joined by a reference. You write M abstractions plus N implementors and compose any pair at runtime, instead of one subclass per shape-renderer combination.
- How is the implementor usually injected in Java?Via the abstraction's constructor (constructor injection), so the bridge is set once at creation and stays final. It can also be a setter for runtime swapping.
saying these in an interview costs you the question
- Saying Bridge is the same as Adapter (Adapter retrofits incompatible APIs; Bridge is designed in advance)
- Thinking 'implementor' means Java's implements keyword rather than the role name
- Claiming both hierarchies are connected by inheritance — they're connected by a held reference (composition)
- Confusing it with Strategy: Strategy swaps an algorithm; Bridge separates a whole abstraction hierarchy from an implementation hierarchy