Free Interactive Course · Design Patterns

Bridge Design Pattern

Split one hierarchy into two that vary independently — so N × M classes become N + M.

Structural Patternsharda.k.a. Handle/Body
ShareXLinkedIn
In one sentence

Decouple an abstraction from its implementation so that the two can vary independently, instead of multiplying into a class for every combination.

01

The problem Bridge solves

The problem

You send three kinds of notification — an alert, a reminder, a one-time passcode — over two channels, SMS and email. Six classes: AlertSms, AlertEmail, ReminderSms, and so on. Slightly silly, but manageable.

Then mobile ships and you add push. Nine classes. Then the growth team wants WhatsApp. Twelve. Then someone adds a shipping update notification, and you write four new classes for one new idea.

The maths is against you: every new notification type costs one class per channel, and every new channel costs one class per notification type. The two things change for completely unrelated reasons — a designer rewords the alert copy, an ops engineer swaps SMS vendors — but they're welded together in the same inheritance tree, so both changes land in the same twelve files.

02

How the Bridge pattern works

Stop putting two independent dimensions in one inheritance tree. Give each its own, and connect them with a reference — the bridge:

  1. Find the two dimensions that are multiplying. Here: what the message is, and how it gets delivered.
  2. One becomes the abstraction: the high-level thing callers use (Notification), free to grow its own subclasses.
  3. The other becomes the implementor: an interface (Channel) with its own implementations, which knows nothing about notifications.
  4. The abstraction holds an implementor and delegates the low-level work to it. Twelve classes become three plus four — and the next channel costs exactly one class.
"Abstraction" here does not mean abstract class. GoF uses the word in its older sense: the higher-level, policy-shaped half of the design — the part that decides what should happen. The implementor is the primitive, mechanism-shaped half that knows how. That naming is the single biggest reason people find Bridge harder than it is; read it as Policy holds Mechanism and the pattern becomes obvious.
ABSTRACTIONIMPLEMENTORNotification- channel : Channel«interface» Channel+ deliver(to, text)the bridgeAlertNotificationOtpNotificationSmsChannelEmailChannel+ push3 notifications × 4 channels = 7 classes, not 12each side grows on its own — a new channel is one class, whatever the other side does
Participants. The Abstraction (Notification) defines the high-level operation and holds an Implementor (Channel). Refined Abstractions are its subclasses; Concrete Implementors are the channel classes. Neither hierarchy knows the other's subclasses — that ignorance is what lets them grow independently.
03

See it: two dimensions, one bridge

The notification below is fixed — an OTP. Change the channel underneath it and watch the calling code stay exactly where it is. Then add a channel that didn't exist when the notification was written.

▶ Try it — swap the channel underneath

An interactive dispatcher: OtpNotification.send() calls channel.deliver() and each channel — SMS, email, push, WhatsApp — handles it, while the notification class itself is never edited. Adding a fourth channel costs one class and zero changes to any notification.

Add both extra channels and read the counter: edits to OtpNotification: 0. Now count what the same two additions would have cost in the original design — one new class per notification type each time, so with three notification types that's six classes instead of two. That ratio is the entire argument for Bridge.
04

Bridge pattern code examples

Two hierarchies, one reference between them — and a new channel that costs one class.

// ── The implementor side: how a message physically leaves the building ──
public interface Channel {
    void deliver(Recipient to, String subject, String body);
}

public final class SmsChannel implements Channel {
    public void deliver(Recipient to, String subject, String body) {
        sms.send(to.phone(), truncate(subject + ": " + body, 160));
    }
}

public final class EmailChannel implements Channel { /* … */ }

// ── The abstraction side: what the message MEANS ────────────────────────
public abstract class Notification {

    protected final Channel channel;      // ← the bridge, one reference

    protected Notification(Channel channel) {
        this.channel = channel;
    }

    public abstract void send(Recipient to);
}

public final class OtpNotification extends Notification {

    private final String code;

    public OtpNotification(Channel channel, String code) {
        super(channel);
        this.code = code;
    }

    @Override public void send(Recipient to) {
        // Policy lives here: expiry wording, no-reply, never log the code.
        channel.deliver(to, "Your verification code",
                        code + " — expires in 10 minutes. Do not share it.");
    }
}

// Composed at the wiring site, and every combination is now legal for free:
Notification otp = new OtpNotification(new SmsChannel(), "481920");

// Adding WhatsAppChannel is ONE new class. No notification changes.
Read across the tabs: Go's embedded interface is the cleanest statement of the pattern of the seven, because the language has no inheritance to confuse it with — and database/sql is a bridge you have already used. C++ knows the pattern under its other name, Handle/Body, and uses a one-implementor version of it (pimpl) for compile times. Python's tab is the honest one: without a compiler forcing the hierarchy, the abstraction side is often just a function that takes the channel — which is still the bridge, minus the ceremony.
05

How to implement Bridge

  1. Identify the two dimensions that are multiplying, and check they really are independent — that they change for different reasons and at different times.
  2. Define the implementor interface with primitive, mechanism-level operations. Keep it narrow: it should be the smallest thing every implementation can honestly do.
  3. Give the abstraction a field holding an implementor, set through the constructor.
  4. Put the policy — wording, sequencing, retries, business rules — in the abstraction, and the mechanism in the implementor. Getting this split wrong is what makes a bridge feel pointless.
  5. Let each side grow its own subclasses. Neither hierarchy should ever name a class from the other.
  6. Choose the pairing at the composition root, or from configuration, so the combination is a runtime decision rather than a compile-time one.
06

When to use Bridge — and when not to

Use it when a class hierarchy is growing along two independent axes, when you want to swap implementations at runtime, when platform-specific code should stay out of your domain classes, or when you want changes to one dimension to stop rippling into the other.

Where it goes wrong

Applying it to one dimension. If there is only ever going to be one implementor, a bridge is an interface, an indirection and a wiring line in exchange for nothing. Wait for the second implementor — it is a cheap refactor at that point, and often it never arrives.

The dimensions weren't independent. If push notifications need a title and SMS doesn't, the implementor interface grows a parameter nobody else uses, then a flag, then a downcast. When the abstraction starts asking which implementor it has, the split was wrong.

A leaky implementor interface. Every method the mechanism side exposes is a method every future implementation must provide. A twelve-method Channel makes writing the next channel a project rather than an afternoon.

Confusing it with Strategy and giving up. The structures really are near-identical. Don't agonise: if you're swapping one algorithm behind one method, call it Strategy; if you're preventing a class explosion across two growing hierarchies, call it Bridge. Nothing in the code depends on which word you use.

You want to…UseBecause
Stop two independent hierarchies multiplying into N × M classesBridgeEach dimension grows on its own side of one reference.
Swap one algorithm behind one operationStrategySame shape, smaller scope — one method, not a hierarchy.
Make an existing incompatible class fitAdapterAdapter is retrofitted after the fact; Bridge is designed in before the explosion.
Create matched families of related objectsAbstract FactoryOften used with Bridge, to build the right implementor for a platform.
Fix the steps of an algorithm and vary the detailsTemplate MethodTemplate Method varies by inheritance; Bridge varies by composition, at runtime.
07

Quick check

🧠 Quick check
You have 4 notification types and 5 channels. How many classes does the bridged design need, and what does adding a sixth channel cost?

In the wild

JavaJDBC — your code calls java.sql.Connection, and every database vendor ships a Driver implementation. The abstraction and the implementors are written by different companies.
Godatabase/sql and driver.Driver — the standard library's bridge, and the reason a Postgres driver author never sees your queries.
PythonDB-API 2.0 (PEP 249) — one interface implemented by sqlite3, psycopg and mysqlclient, so ORMs target the abstraction and never a database.
C#ADO.NET providers behind DbConnection, and ILogger with its interchangeable logging providers.
JavaScriptReact's renderer split — the same component tree drawn by react-dom, react-native or a custom reconciler. Components are the abstraction; renderers are implementors.
C++Qt's platform abstraction (QPA) putting one widget API over Windows, macOS and X11 — and the pimpl idiom, a one-implementor bridge used for compile-time isolation.

Frequently asked questions

What is the difference between the Bridge and Strategy patterns?
Almost nothing structurally — both are an object holding an interface it delegates to — so the difference is intent and scale. Strategy is behavioural: it swaps one interchangeable algorithm behind one operation, often several times during an object's life. Bridge is structural: it splits two hierarchies that would otherwise multiply, and the implementor is usually chosen once at construction. A practical test: if your interface has one method and several algorithms for it, that's Strategy; if both sides have their own growing family of subclasses, that's Bridge.
What does "abstraction" mean in the Bridge pattern?
Not abstract class. GoF means the higher-level half of the design — the part that defines what should happen in the caller's language ("send a one-time passcode"). The implementor is the primitive half that knows how ("put 160 characters into an SMS"). The abstraction can be a perfectly ordinary concrete class. Reading it as policy holds mechanism removes most of the confusion around this pattern.
When should I not use the Bridge pattern?
When there's only one implementation and no concrete plan for a second — you're paying an interface and an indirection for a flexibility nobody asked for, and introducing it later is a small refactor. Also skip it when the two dimensions aren't genuinely independent: if the abstraction keeps needing to know which implementor it has, the seam is in the wrong place and the bridge will leak parameters and flags until someone deletes it.
Is the pimpl idiom the Bridge pattern?
It's a degenerate case of it — one abstraction, one implementor, adopted for a completely different reason. A C++ class that holds a unique_ptr<Impl> has the Bridge's structure (GoF's alternative name, Handle/Body, describes it exactly), but the goal is compile-time isolation and ABI stability rather than preventing a class explosion. If a second implementor ever appears, it becomes a full bridge with no structural change at all.
How does Bridge differ from Adapter?
Timing and intent. Adapter is retrofitted: two things already exist, their interfaces don't match, and you write a translator. Bridge is designed in before either side has many implementations, specifically so that the interfaces never diverge and both sides can grow. Adapter fixes a problem you have; Bridge prevents one you can see coming.

Finish the handbook, earn the certificate

All 23 Gang-of-Four patterns, each with a simulator you can click and code in seven languages. Free, no signup.

See all 23 patterns →
Found this course useful? Share it.
ShareXLinkedIn

Comments

0

Join the conversation. Sign in to leave a comment — we'd love to hear your thoughts.