Bridge Pattern
Overview
Normal and urgent notices can go through SMS or mail. A class for every combination gives four classes immediately, and another channel adds one class per notice kind.
Bridge separates the two dimensions and connects their objects. A notice determines the message, while a channel supplies the delivery implementation.
UML
Notice holds an IChannel. NormalNotice and UrgentNotice extend the abstraction side; SmsChannel and MailChannel extend delivery. The constructor connects the two.
History
GoF cataloged Bridge as a structural pattern in Design Patterns in 1994. It separates an abstraction from its implementation so each can vary independently.1
Why use it?
Notice rules do not need a copy for every channel. Adding a channel also leaves the notice classes alone. This helps when both dimensions genuinely change independently.
Adapter reconciles an existing incompatible contract. Bridge is a design separation between two dimensions. Both may wrap objects, so their purpose distinguishes them better than their shape.
Example
Connect a normal notice to SMS. The delivery classes return strings instead of contacting a messaging service.
using System;
using System.Collections.Generic;
using System.Linq;
Console.WriteLine(new NormalNotice(new SmsChannel()).Send("ready"));
interface IChannel { string Deliver(string message); }
sealed class SmsChannel : IChannel
{
public string Deliver(string message) => "SMS: " + message;
}
sealed class MailChannel : IChannel
{
public string Deliver(string message) => "MAIL: " + message;
}
abstract class Notice(IChannel channel)
{
protected IChannel Channel { get; } = channel;
public abstract string Send(string message);
}
sealed class NormalNotice(IChannel channel) : Notice(channel)
{
public override string Send(string message) => Channel.Deliver(message);
}
sealed class UrgentNotice(IChannel channel) : Notice(channel)
{
public override string Send(string message) => Channel.Deliver("URGENT " + message);
}
SMS: readyNormalNotice uses IChannel.Deliver() without knowing an SMS API.
An extended example
Combine two notice kinds with two channels. The urgent prefix remains in UrgentNotice alone.
using System;
using System.Collections.Generic;
using System.Linq;
foreach (IChannel channel in new IChannel[] { new SmsChannel(), new MailChannel() })
{
Console.WriteLine(new NormalNotice(channel).Send("ready"));
Console.WriteLine(new UrgentNotice(channel).Send("ready"));
}
interface IChannel { string Deliver(string message); }
sealed class SmsChannel : IChannel
{
public string Deliver(string message) => "SMS: " + message;
}
sealed class MailChannel : IChannel
{
public string Deliver(string message) => "MAIL: " + message;
}
abstract class Notice(IChannel channel)
{
protected IChannel Channel { get; } = channel;
public abstract string Send(string message);
}
sealed class NormalNotice(IChannel channel) : Notice(channel)
{
public override string Send(string message) => Channel.Deliver(message);
}
sealed class UrgentNotice(IChannel channel) : Notice(channel)
{
public override string Send(string message) => Channel.Deliver("URGENT " + message);
}
SMS: ready
SMS: URGENT ready
MAIL: ready
MAIL: URGENT readyFour combinations do not require four combination-specific classes. Real delivery also needs failure, cancellation and retry contracts; these stand-ins send no external messages.
Related reading
Each example is a separate .NET 10 console program.
Footnotes
- Gamma, Helm, Johnson, Vlissides. Design Patterns (1994). Publisher record. Structural patterns, Bridge. β©