Bridge
Split a class that varies along two independent axes into two hierarchies - control and platform - joined by a reference, so each can grow without touching the other.
The problem
Start with a Shape class and two subclasses, Circle and Square. Now the product
owner wants colors, so you add Red and Blue. Except you cannot simply add two
classes: you need RedCircle, BlueCircle, RedSquare, BlueSquare. Add a triangle
and you owe two more. Add green and you owe three more. The hierarchy grows like a
multiplication table because you are trying to vary along two independent dimensions
with a mechanism that only has one.
The grown-up version is a cross-platform app: several GUIs (customer, admin) times several operating systems (Windows, Linux, macOS), usually held together by conditionals sprayed across the codebase.
The solution
Pull one dimension out into its own hierarchy and have the original class hold a
reference to it. Colors become a Color hierarchy; Shape gets a color field and
delegates anything color-shaped to it. Adding a color stops touching shapes, and adding
a shape stops touching colors.
The GoF names for the two halves are abstraction (the high-level control layer, which should do no real work) and implementation (the platform layer that does). Ignore the academic ring: think remote control and device. The abstraction usually declares rich operations built from the primitive ones the implementation offers.
- 1The client picks a platform. This is the only moment anyone names a concrete implementation.
- 2The chosen device is handed to the remote constructor. That reference is the bridge.
- 3From here on the client talks only to the abstraction. It has forgotten what is on the other end.
- 4The remote asks a primitive question through the Device interface, never through the Radio class.
- 5One high-level intent, "toggle", becomes a decision plus a low-level call. That split is the whole pattern.
- 6A refined-abstraction feature that lives purely in the control hierarchy.
- 7Mute is expressed with primitives every device already supports, so no device needed changing to gain it.
Structure
Four moving parts: the abstraction with its control logic, refined abstractions for control variants, the implementation interface listing primitives, and concrete implementations holding platform-specific code. The client links one of each and then speaks only to the abstraction.
Code
Remotes on one side, devices on the other, one reference field in between.
When to use it
- A monolithic class carries several variants of the same functionality - one class that must work against MySQL, Postgres and SQLite, for instance - and every change risks the others.
- You need to extend a class along two or more orthogonal dimensions. Give each dimension its own hierarchy and let the original delegate.
- You want to swap implementations at runtime. Assigning a new value to the reference field is the whole operation.
Pitfalls
- Bridging a cohesive class. If the class genuinely has one axis of variation, the split adds indirection and subtracts clarity.
- A mirror-image implementation interface. If every abstraction method delegates one-to-one, the implementation layer is decoration, not design. Push primitives down and compose them up.
- Leaking the platform. The moment the abstraction type-checks for
Tvor reads a device-specific field, the two hierarchies are welded together again.
Don't confuse it with
- Strategy. Same diagram, different problem. Strategy plugs one interchangeable algorithm into one class. Bridge cuts a class in half so two hierarchies can evolve on their own schedules. If you can only extend one side, it is Strategy.
- Adapter. Bridge is designed before the collision happens; Adapter is repair work after it. Bridge partners are built to fit; Adapter partners never were.
- State. State also delegates to a swappable object, but state objects know the other states and hand control between them. Bridge implementations are peers that never mention each other.
- Abstract Factory. Frequently paired with Bridge rather than confused with it: when only certain abstraction and implementation combinations are legal, a factory can encapsulate the matchmaking and keep the client out of it.
Check yourself
You have 4 report layouts and 5 storage backends. What does Bridge do to the class count?