Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Design Patterns (GoF and modern)  Module 3 – Structural patterns: Adapter to Proxy

Bridge: split abstraction from implementation

Replace a class for every pair of message kind and delivery channel with two hierarchies joined by composition, and tell Bridge apart from look-alikes.

  • Intermediate
  • 25 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0, quickjs 0.32.0 and Clang 21.0.0
  • By MySmartCoPilot
Free core, Pro depth: 8 of 25 minutes free
Free: The class explosion: three kinds times three channels · Bridge in code · When a Bridge is worth it · Bridge and the patterns that look like it. With a pass: 3 in-depth parts and 3 interview answers, about 17 minutes.

What you will learn

  • Replace a class explosion of types times platforms with two hierarchies joined by composition
  • Implement Bridge so the abstraction and its implementation vary independently at run time
  • Explain how the C++ Pimpl idiom relates to Bridge and measure what it saves and costs
  • Distinguish Bridge from Adapter and Strategy by intent

Before you start

What you’ll learn · Before you start

What you will learn

  • Replace a class explosion of types times platforms with two hierarchies joined by composition
  • Implement Bridge so the abstraction and its implementation vary independently at run time
  • Explain how the C++ Pimpl idiom relates to Bridge and measure what it saves and costs
  • Distinguish Bridge from Adapter and Strategy by intent

Before you start

On this page

Bridge splits a class hierarchy that grows in two directions into two hierarchies joined by composition. One side, the abstraction, is what clients use: here, the kinds of message an application sends, such as alerts, reminders and receipts. The other side, the implementation, is how the work gets done: delivery channels such as email, text messages and chat. Each message holds a channel object and calls it, so a new channel or a new kind of message is one new class, and the two sides vary independently, even while the program runs. Bridge is a design decision made up front, when you can see both directions of change coming; Adapter makes an existing interface fit after the fact.

Python 3.14.8, Node.js 24.21.0 and Apple clang 21.0.0 ran the programs below, and most of the Python and JavaScript ones can be run again in your browser.

The class explosion: three kinds times three channels

A natural first design gives each combination its own subclass: AlertEmail, AlertSms, AlertChat, ReminderEmail and so on, nine classes for three kinds and three channels. Each class repeats the formatting of its channel, so the rule for a text message lives in three places. The count grows as a product: a fourth channel adds three classes, a fourth kind after it adds four, and five kinds over four channels need 20. In general, n kinds and m channels need n × m subclasses.

Bridge turns the product into a sum. The kinds of message form one hierarchy and the channels another, and the base class of the kinds holds a reference to a channel. That reference is the bridge:

Message kinds extend Message, channels implement Channel, and each Message holds one Channel: the bridge.What is said: the abstractionHow it is delivered: the implementationAlert, Reminder, Receipttitle() and body()Messagesend(to)Channeldeliver(to, title, body)Email, Sms, Chatextendimplementholds one: the bridge

Two hierarchies joined by one reference

Text description of the diagram

The diagram shows two groups of boxes.

  1. The upper group is what is said, the abstraction. Alert, Reminder and Receipt, which supply a title and a body, extend Message, whose send(to) method sends them.
  2. The lower group is how it is delivered, the implementation. Email, Sms and Chat implement Channel, whose deliver(to, title, body) method delivers a message.
  3. An arrow from Message to Channel, labelled "holds one: the bridge", joins the two groups: each message holds a channel object and calls it.

New kinds of message are added to the upper group and new channels to the lower group, without touching the other.

Bridge in code

Message is the abstraction. Its send() asks its channel to deliver the message’s title and body, and each kind supplies only those two parts. Channel is the implementation side, and each channel formats a delivery its own way:

Three kinds of message and three channels, as a Bridge Python · free/bridge.py
"""Three kinds of message times three delivery channels, as a Bridge: 3 + 3 classes instead of 3 x 3 subclasses."""

from abc import ABC, abstractmethod


class Channel(ABC):
    """The implementation side: how a message reaches someone."""

    @abstractmethod
    def deliver(self, to: str, title: str, body: str) -> str: ...


class Email(Channel):
    def deliver(self, to, title, body):
        return f"email to {to}: subject {title!r}, {body}"


class Sms(Channel):
    def deliver(self, to, title, body):
        return f"sms to {to}: {title}: {body}"


class Chat(Channel):
    def deliver(self, to, title, body):
        return f"chat to {to}: *{title}* {body}"


class Message(ABC):
    """The abstraction side: what is said. It holds a channel and does not know which one."""

    def __init__(self, channel: Channel) -> None:
        self.channel = channel

    def send(self, to: str) -> str:
        return self.channel.deliver(to, self.title(), self.body())

    @abstractmethod
    def title(self) -> str: ...

    @abstractmethod
    def body(self) -> str: ...


class Alert(Message):
    def __init__(self, channel: Channel, problem: str) -> None:
        super().__init__(channel)
        self.problem = problem

    def title(self):
        return "Alert"

    def body(self):
        return self.problem


class Reminder(Message):
    def __init__(self, channel: Channel, event: str, at: str) -> None:
        super().__init__(channel)
        self.event, self.at = event, at

    def title(self):
        return "Reminder"

    def body(self):
        return f"{self.event} at {self.at}"


class Receipt(Message):
    def __init__(self, channel: Channel, order: int) -> None:
        super().__init__(channel)
        self.order = order

    def title(self):
        return "Receipt"

    def body(self):
        return f"order {self.order} is paid"


for channel in (Email(), Sms(), Chat()):
    for message in (Alert(channel, "disk 91% full"), Reminder(channel, "stand-up", "09:30"), Receipt(channel, 1042)):
        print(message.send("asha"))

kinds, channels = len(Message.__subclasses__()), len(Channel.__subclasses__())
print(f"classes: {kinds} kinds + {channels} channels = {kinds + channels}; a subclass per pair would be {kinds * channels}")

reminder = Reminder(Email(), "stand-up", "09:30")
reminder.channel = Sms()  # the implementation changes while the program runs
print(reminder.send("asha"))

Output

email to asha: subject 'Alert', disk 91% full
email to asha: subject 'Reminder', stand-up at 09:30
email to asha: subject 'Receipt', order 1042 is paid
sms to asha: Alert: disk 91% full
sms to asha: Reminder: stand-up at 09:30
sms to asha: Receipt: order 1042 is paid
chat to asha: *Alert* disk 91% full
chat to asha: *Reminder* stand-up at 09:30
chat to asha: *Receipt* order 1042 is paid
classes: 3 kinds + 3 channels = 6; a subclass per pair would be 9
sms to asha: Reminder: stand-up at 09:30

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 bridge.py

The nine lines are every pairing of kind and channel, and they come from six classes, counted by the program from its own class hierarchy. The last line is the same reminder sent again after its channel was replaced, without a new object or a new class. That is the intent of the pattern: the abstraction and the implementation vary independently. With n kinds and m channels, a Bridge needs n + m classes instead of n × m, so five kinds over four channels need 9 classes instead of 20.

When a Bridge is worth it

Reach for a Bridge when you can name two independent directions of change before you write the classes, and both will grow: kinds of message and channels, shapes and renderers, reports and output formats, devices and their remote controls. If only one side will ever vary, a plain interface is enough. If the second side has one member and no second one is in sight, the extra layer is overhead you pay for nothing. The running cost of a Bridge is small: one more object per message and one more call per operation. The real work is designing the implementation’s interface, because every kind of message must be expressible through it.

Bridge and the patterns that look like it

Several patterns share the shape of an object that holds another object and delegates to it. What tells them apart is intent:

  • Adapter makes an existing interface fit another one after both exist; a Bridge is planned before either side is written.
  • Strategy swaps one algorithm inside an object, such as a sort order; a Bridge implementation is a whole platform or back end with several operations.
  • Pimpl, a C++ idiom, is a Bridge with a single implementation: the header holds only a pointer, so the private members can change without recompiling the code that uses the class.

Check yourself

3 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 3 The application has three kinds of message and three channels, and a fourth channel, Push, is added. How many new classes does each design need?

    Choose one answer.

    Show the answer to question 1

    Answer: Bridge 1, one subclass per pair 3

    In the Bridge the new channel is one class that implements Channel, and every kind of message can use it at once. With a subclass per pair, each of the three kinds needs its own Push class: AlertPush, ReminderPush and ReceiptPush.

  2. Question 2 of 3 Using the classes of bridge.py, what does this print?

    Read the code, then choose one answer.

    r = Reminder(Email(), "retro", "16:00")
    r.channel = Chat()
    print(r.send("ravi"))
    Show the answer to question 2

    Answer: chat to ravi: Reminder retro at 16:00

    send() asks whatever channel the message holds at the moment of the call, so after the assignment the chat channel formats the reminder's own title and body. The message object and its class stay the same; only the implementation it holds has changed.

  3. Question 3 of 3 Which statement describes what makes bridge.py a Bridge?

    Choose one answer.

    Show the answer to question 3

    Answer: Each message holds a channel object and calls it, so kinds and channels are two hierarchies that grow separately

    The pattern is the reference from the abstraction to the implementation: composition instead of inheritance, with each side growing on its own. Translating a foreign interface is what an adapter does, and picking one algorithm is a strategy.

Designing the implementation side: primitives, room and switching Pro: opens with a Pro, Premium or Ultimate pass

A Bridge is only as good as the interface between its two sides. This part designs that interface for channels that cannot all carry the same text, and moves messages to another channel while they are being sent.

In this part:

  • Primitive operations, not one method per kind of message
  • A channel that cannot carry every text: room measured in characters
  • Switching the channel while messages are queued

2 runnable examples (JavaScript, Python) · about 3 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Pimpl in C++: a Bridge with one implementation, measured on a build Pro: opens with a Pro, Premium or Ultimate pass

In C++ the Bridge has a second use that has nothing to do with several implementations: hiding a class’s private members from its header. This part builds the same program with and without Pimpl and counts what a change costs.

In this part:

  • Why private members cost a rebuild
  • Four files against one: the rebuild and the size, measured
  • What Pimpl costs, and when it pays

3 runnable examples (C header, C++, Python) · about 3 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Bridge, Adapter or Strategy: telling them apart by change requests Pro: opens with a Pro, Premium or Ultimate pass

Bridge, Adapter and Strategy all hold an object and delegate to it, so the code alone often cannot tell them apart. This part separates them by intent and by the changes each one makes cheap, with the classes counted.

In this part:

  • The same shape, three intents
  • Four change requests, with the classes counted
  • Split shapes from the formats that draw them
    • Results of the sample tests

1 runnable example (Python) · 1 exercise with sample tests · about 5 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Interview questions

  1. Mid to senior

    A notification module has twelve classes such as EmailAlert, SmsAlert and PushReceipt, and a new channel is planned. Walk through refactoring it into a Bridge. What goes in the channel interface, and how do you make sure every message still looks exactly as before?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Mid to senior

    A colleague says your Bridge is just Strategy with extra steps. When are they right, and when is the difference real? Use a document exporter with several output formats as the example.

    Model answer, Pro. First step and follow-ups

  3. Senior to staff

    You ship a C++ library as a shared library. A release added one private member to a public class, and applications that were not rebuilt started to crash. Explain why, and how the Pimpl idiom would have prevented it. What does Pimpl cost?

    Model answer, Pro. First step and follow-ups

Key takeaways

  • Bridge splits a hierarchy that grows in two directions into an abstraction and an implementation, joined by a reference from one to the other.
  • n kinds and m implementations cost n + m classes with a Bridge, and n × m with a subclass for every pair.
  • The abstraction holds its implementation in a field, so the implementation can be replaced while the program runs.
  • Bridge is planned up front, Adapter fits existing interfaces together, and Strategy varies one algorithm.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.