🧩 Bridge × Strategy
✅ 組み合わせの意図
Bridge
パターンによって「通知手段(メール / Slack など)」と「通知内容の表現(プレーン / HTML)」という独立した 2 軸を分離Strategy
パターンで通知内容の組み立て方を外部化し、通知方式 × 表現方法の柔軟な組み合わせを可能にする
共通の通知処理を Notifier
でまとめつつ、出力方式(メール / Slack)とメッセージの構成ロジック(プレーン / HTML)を柔軟に組み合わせられる設計を実現。
✅ よく使われるシーン
- 通知の表現方式(テキスト / HTML / Markdown)と送信方式(メール / Slack など)を自由に組み合わせたいとき
- 拡張の軸が複数あるが、それぞれを独立して設計・拡張したいとき
- UI とテーマ、データと表示手段など、独立した 2 軸を持つ構成で再利用性を高めたいとき
✅ UML クラス図
✅ コード例
- TypeScript
- PHP
- Python
interface PaymentStrategy {
pay(amount: number): void;
}
class CreditCardPayment implements PaymentStrategy {
pay(amount: number): void {
console.log(`[CreditCard] Paid ${amount}`);
}
}
class PayPalPayment implements PaymentStrategy {
pay(amount: number): void {
console.log(`[PayPal] Paid ${amount}`);
}
}
interface PaymentState {
handle(amount: number): void;
setStrategy(strategy: PaymentStrategy): void;
}
class ReadyState implements PaymentState {
constructor(private strategy: PaymentStrategy) {}
handle(amount: number): void {
console.log("Ready to process payment");
this.strategy.pay(amount);
}
setStrategy(strategy: PaymentStrategy): void {
this.strategy = strategy;
}
}
class DisabledState implements PaymentState {
handle(amount: number): void {
console.log("Payment is currently disabled");
}
setStrategy(strategy: PaymentStrategy): void {
console.log("Cannot set strategy while disabled");
}
}
class PaymentContext {
constructor(private state: PaymentState) {}
setState(state: PaymentState) {
this.state = state;
}
setStrategy(strategy: PaymentStrategy) {
this.state.setStrategy(strategy);
}
pay(amount: number) {
this.state.handle(amount);
}
}
// Usage
const context = new PaymentContext(new ReadyState(new CreditCardPayment()));
context.pay(100);
context.setStrategy(new PayPalPayment());
context.pay(200);
context.setState(new DisabledState());
context.pay(300);
<?php
interface PaymentStrategy {
public function pay(int $amount): void;
}
class CreditCardPayment implements PaymentStrategy {
public function pay(int $amount): void {
echo "[CreditCard] Paid {$amount}\n";
}
}
class PayPalPayment implements PaymentStrategy {
public function pay(int $amount): void {
echo "[PayPal] Paid {$amount}\n";
}
}
interface PaymentState {
public function handle(int $amount): void;
public function setStrategy(PaymentStrategy $strategy): void;
}
class ReadyState implements PaymentState {
private PaymentStrategy $strategy;
public function __construct(PaymentStrategy $strategy) {
$this->strategy = $strategy;
}
public function handle(int $amount): void {
echo "Ready to process payment\n";
$this->strategy->pay($amount);
}
public function setStrategy(PaymentStrategy $strategy): void {
$this->strategy = $strategy;
}
}
class DisabledState implements PaymentState {
public function handle(int $amount): void {
echo "Payment is currently disabled\n";
}
public function setStrategy(PaymentStrategy $strategy): void {
echo "Cannot set strategy while disabled\n";
}
}
class PaymentContext {
private PaymentState $state;
public function __construct(PaymentState $state) {
$this->state = $state;
}
public function setState(PaymentState $state): void {
$this->state = $state;
}
public function setStrategy(PaymentStrategy $strategy): void {
$this->state->setStrategy($strategy);
}
public function pay(int $amount): void {
$this->state->handle($amount);
}
}
// Usage
$context = new PaymentContext(new ReadyState(new CreditCardPayment()));
$context->pay(100);
$context->setStrategy(new PayPalPayment());
$context->pay(200);
$context->setState(new DisabledState());
$context->pay(300);
from abc import ABC, abstractmethod
class PaymentStrategy(ABC):
@abstractmethod
def pay(self, amount: int) -> None:
pass
class CreditCardPayment(PaymentStrategy):
def pay(self, amount: int) -> None:
print(f"[CreditCard] Paid {amount}")
class PayPalPayment(PaymentStrategy):
def pay(self, amount: int) -> None:
print(f"[PayPal] Paid {amount}")
class PaymentState(ABC):
@abstractmethod
def handle(self, amount: int) -> None:
pass
@abstractmethod
def set_strategy(self, strategy: PaymentStrategy) -> None:
pass
class ReadyState(PaymentState):
def __init__(self, strategy: PaymentStrategy):
self.strategy = strategy
def handle(self, amount: int) -> None:
print("Ready to process payment")
self.strategy.pay(amount)
def set_strategy(self, strategy: PaymentStrategy) -> None:
self.strategy = strategy
class DisabledState(PaymentState):
def handle(self, amount: int) -> None:
print("Payment is currently disabled")
def set_strategy(self, strategy: PaymentStrategy) -> None:
print("Cannot set strategy while disabled")
class PaymentContext:
def __init__(self, state: PaymentState):
self.state = state
def set_state(self, state: PaymentState) -> None:
self.state = state
def set_strategy(self, strategy: PaymentStrategy) -> None:
self.state.set_strategy(strategy)
def pay(self, amount: int) -> None:
self.state.handle(amount)
# Usage
context = PaymentContext(ReadyState(CreditCardPayment()))
context.pay(100)
context.set_strategy(PayPalPayment())
context.pay(200)
context.set_state(DisabledState())
context.pay(300)
✅ 解説
Notifier
は、通知手段の抽象クラス。内部にMessageStrategy
を持ち、メッセージの組み立て方を外部化(Strategy
)。EmailNotifier
,SlackNotifier
はそれぞれ異なる通知チャネルを提供(Bridge
の実装側)。MessageStrategy
はメッセージのフォーマット戦略で、PlainMessageStrategy
やHtmlMessageStrategy
によって差し替え可能。- 通知方式とメッセージ構成方式が独立して変更・拡張できる構造になっている。
✅ まとめ
Bridge
により「通知方式の拡張」と「通知メッセージ構成」の関心を分離Strategy
によりメッセージ内容の構築方法を切り替え可能- 拡張の自由度が高く、「どの通知方式 × どの表現方式」という柔軟な組み合わせを可能にする
この構造は、多軸での拡張が求められる実務において非常に効果的で、設計の保守性・再利用性を大きく向上させる。