codejump

AcademyDesign Patterns

Bridge pattern

Letting two dimensions of variation grow independently instead of multiplying into N times M classes, the two hierarchies it costs, and the if/else sneaking that growth back.

6 min readUpdated

3 exercises

Waiting at the end of this page.

Bridge solves a problem the wrap-and-forward patterns don't touch: two independent ways an object can vary that would otherwise multiply against each other instead of simply adding.

Without Bridge, "shape" times "color" needs one subclass per combination: a class for a red circle, another for a blue circle, another for a red square, and every new color multiplies every existing shape. Bridge splits the two dimensions into separate hierarchies and connects them through composition instead of inheritance. A shape holds a reference to a color instead of extending one, so N shapes and M colors need N + M classes, never N × M.

The problem it solves

Your app sends notifications. There are different kinds (an alert, a reminder) and different channels (email, SMS, push). If each combination is its own subclass, you write AlertEmail, AlertSms, AlertPush, ReminderEmail, ReminderSms, ReminderPush.

  • Six classes for two kinds and three channels. Add a fourth channel and you write two more. Add a third kind and you write four more.
  • Duplicated code. The email logic is copied into every kind that supports email.
  • Rigid at runtime. A notification cannot change channel after it is created, because the channel is baked into its class.

The two dimensions have nothing to do with each other. They should not be tied into one hierarchy.

Structure

Shapes hold a reference to a Color, so the two hierarchies grow independently. The reference is the bridge
Shapes hold a reference to a Color, so the two hierarchies grow independently. The reference is the bridge

Four parts:

  • The abstraction (Circle, Square): the high-level side, which holds a reference to the implementation.
  • The implementation interface (Color): the low-level operations the abstraction relies on.
  • Concrete implementations (Red, Blue).
  • Refined abstractions: extra kinds of abstraction, each free to work with any implementation.
class Red {
  fill() { return 'red'; }
}
class Blue {
  fill() { return 'blue'; }
}

class Circle {
  constructor(color) { this.color = color; }
  draw() { return 'circle filled ' + this.color.fill(); }
}
class Square {
  constructor(color) { this.color = color; }
  draw() { return 'square filled ' + this.color.fill(); }
}

// new Circle(new Red()).draw()  → 'circle filled red'
// new Square(new Blue()).draw() → 'square filled blue'

Add a third color and nothing about Circle or Square changes. Only a new Color implementation appears. That is the payoff Bridge is named for: the reference a shape holds to a color is the bridge, and it lets the two hierarchies grow independently instead of multiplying each other.

Where it is used in real systems

Two kinds of notification and three channels need 2 + 3 classes instead of 2 × 3. Each kind can use any channel
Two kinds of notification and three channels need 2 + 3 classes instead of 2 × 3. Each kind can use any channel
Use caseThe two dimensions
NotificationsKind (alert, reminder) and channel (email, SMS, push).
Cross-platform UIWidget type (button, dialog) and platform renderer (web, desktop).
LoggingLogger type (audit, error) and output (console, file, remote service).
PaymentsPayment flow (one-off, subscription) and provider (Stripe, PayPal).
Remote controlsRemote type (basic, advanced) and device (TV, radio).
Database accessRepository type (users, orders) and the storage engine behind it.
ReportsReport type (sales, usage) and export format (PDF, CSV).

Whenever you catch yourself naming a class with two adjectives, such as PdfSalesReport, ask whether two things are varying independently.

Bridges you already use

  • Logging libraries. A logger (the abstraction) writes through transports (the implementation): console, file, a hosted service. You pick each independently, and pino or Winston work this way.
  • Graphics and UI toolkits. A drawing API that renders to canvas, SVG or a PDF through interchangeable backends.
  • JDBC and database drivers. One query API, many interchangeable drivers underneath.
  • Dependency injection. Passing a Sender into a notification's constructor is Bridge, whether or not it was called that.

Bridge or Strategy

The code looks nearly the same, which is why interviewers ask.

  • Strategy is about swapping one algorithm inside one class. One dimension.
  • Bridge is a structural decision made up front: two hierarchies that evolve separately, designed to avoid a class explosion. Two dimensions.

If you are choosing how one thing is done, it is Strategy. If you are preventing a grid of subclasses, it is Bridge.

When not to use it

  • Only one dimension varies. With just one hierarchy, Bridge adds an interface and a reference for nothing.
  • The combinations are few and fixed. Two shapes and two colors that will never grow are simpler as four classes.
  • The split is artificial. If the "implementation" is always chosen by the abstraction, they are one thing and the interface hides nothing.
  • Design-time only. It needs foresight. Retrofitting it into a large existing hierarchy is a real refactor.

What interviewers ask

  • "What is the Bridge pattern?" Separating an abstraction from its implementation so both can vary independently.
  • "What problem does it solve?" The class explosion from combining two dimensions through inheritance.
  • "Bridge versus Adapter?" Adapter fixes an incompatibility after the fact. Bridge is designed up front so the parts stay independent.
  • "Bridge versus Strategy?" Strategy swaps an algorithm. Bridge separates two hierarchies.
  • "Give an example." Notifications by kind and channel, or a logger and its transports.

The mistake to avoid

A Bridge collapsed back into one hierarchy the moment a shape starts branching on its own color with an if/else. If Circle.draw() contains if (color === 'red'), the abstraction knows about the implementation and the bridge is gone. Every decision about color belongs behind the Color interface.

The mechanism above, an object holding a reference to another and delegating to it, is the same in JavaScript, Python or Java. Only the syntax changes.

Now practice it

1 challenge · 2 quizzes

Reading this page is the cheap half. These are the exercises that make you use it.