브리지 패턴
개요
일반 알림과 긴급 알림을 문자와 메일로 보낸다고 하자. 조합마다 클래스를 만들면 일반문자·긴급문자·일반메일·긴급메일이 생기고, 채널을 추가할 때마다 알림 종류만큼 구현이 늘어난다.
브리지(Bridge)는 두 변화 축을 나누어 객체 참조로 연결한다. 여기서는 알림이 내용을 정하고 채널이 전달 방법을 맡으며, 둘을 조립해 필요한 조합을 만든다.
UML
Notice는 IChannel을 참조한다. NormalNotice와 UrgentNotice는 알림 쪽을, SmsChannel과 MailChannel은 전송 쪽을 확장한다. 연결은 Notice의 생성자에서 정한다.
역사
GoF는 1994년 Design Patterns에서 Bridge를 구조 패턴으로 정리했다. 추상화와 구현을 분리해 각각 확장할 수 있게 하는 구조다.1
왜 사용하는가?
알림 규칙을 바꿀 때 채널별 코드를 복사하지 않고, 새 채널을 넣을 때도 알림 종류를 다시 만들지 않는다. 두 축이 실제로 독립해서 변할 때 이 분리가 의미를 가진다.
어댑터가 이미 다른 약속을 가진 대상을 맞추는 데 초점을 둔다면, 여기서는 처음부터 두 축을 독립적으로 설계한다. 둘 다 객체를 감쌀 수 있으므로 모양보다 해결하려는 문제로 구별한다.
예제
일반 알림을 문자 채널에 연결한다. 전송 클래스는 실제 통신 대신 결과 문자열을 반환한다.
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는 문자를 보내는 세부 API를 모르고 IChannel.Deliver()만 사용한다.
범위 넓힌 예제
알림 두 종류와 채널 두 종류를 조합한다. 긴급 표시를 붙이는 규칙은 UrgentNotice 한곳에 있다.
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 ready네 조합을 표현하기 위해 네 전용 클래스를 만들지 않았다. 실제 전송에는 실패·취소·재시도 계약이 추가로 필요하며, 이 대역은 외부 메시지를 보내지 않는다.
같이 보기
각 예제는 독립된 .NET 10 콘솔 프로그램이다.