ExampleDifficulty: Intermediate2 min

Bridge Example: Notifications by Urgency and by Channel

Urgent and routine messages, each sent by email, SMS or push, without a class per pair.

Notifications that vary by urgency and by channel are a natural Bridge, and this example maps the template onto it. A routine notification is sent once. An urgent one is sent twice, the second time as a reminder. Either can go by email, by SMS, which cuts the text to 30 characters, or by push.

The template's participants in the notifications example
TemplateNotifications example
AbstractionNotification, which holds a channel and sends once
RefinedAbstractionUrgentNotification, which sends the message and a reminder
ImplementorChannel, with deliver
ConcreteImplementorEmail, Sms and Push
py
from abc import ABC, abstractmethod


class Channel(ABC):
    @abstractmethod
    def deliver(self, recipient: str, text: str) -> None: ...


class Email(Channel):
    def deliver(self, recipient: str, text: str) -> None:
        print(f"email to {recipient}: {text}")


class Sms(Channel):
    def deliver(self, recipient: str, text: str) -> None:
        print(f"sms to {recipient}: {text[:30]}")


class Push(Channel):
    def deliver(self, recipient: str, text: str) -> None:
        print(f"push to {recipient}: {text}")


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

    def send(self, recipient: str, text: str) -> None:
        self._channel.deliver(recipient, text)


class UrgentNotification(Notification):
    def send(self, recipient: str, text: str) -> None:
        self._channel.deliver(recipient, f"URGENT {text}")
        self._channel.deliver(recipient, f"URGENT reminder: {text}")


Notification(Email()).send("ana", "Your invoice is ready")
UrgentNotification(Sms()).send("ben", "Server db-2 is down since 02:14 UTC")
UrgentNotification(Push()).send("ben", "Server db-2 is down")

kinds, channels = 2, 3
print(f"one subclass per pair: {kinds * channels}, with a bridge: {kinds + channels}")
kinds, channels = 2, 4
print(f"after a fourth channel: {kinds * channels} against {kinds + channels}")
OutputRan with python:3.12-slim (Python 3.12.13)
email to ana: Your invoice is ready
sms to ben: URGENT Server db-2 is down sin
sms to ben: URGENT reminder: Server db-2 i
push to ben: URGENT Server db-2 is down
push to ben: URGENT reminder: Server db-2 is down
one subclass per pair: 6, with a bridge: 5
after a fourth channel: 8 against 6
`UrgentNotification` decides how often to send; the channel decides how one message is delivered.

The urgent message by SMS shows both sides at work: the abstraction sent it twice, and the channel cut each copy to 30 characters. Neither class knows about the other's rule.

Classes needed for two kinds of notification, with and without a bridge
ChannelsOne subclass per pairWith a bridge
365
486
5107

The first two rows are the listing's own last lines. Each new channel costs two classes without a bridge, one per kind of notification, and one class with it. A third kind, such as a digest that batches messages, would cost one class per channel without a bridge, and one with it.