ストラテジーパターン
アルゴリズムをオブジェクトとしてカプセル化し、実行時に切り替え可能にするデザインパターン
設計パターンオブジェクト指向
ストラテジーパターンとは
ストラテジーパターンは、アルゴリズムをオブジェクト (関数) としてカプセル化し、実行時に切り替え可能にするデザインパターンである。GoF デザインパターンの振る舞いパターンに分類される。
問題: 条件分岐の肥大化
// ❌ 条件分岐が増え続ける
function calculatePrice(type: string, base: number): number {
if (type === 'regular') return base;
if (type === 'premium') return base * 0.9;
if (type === 'vip') return base * 0.8;
if (type === 'employee') return base * 0.5;
// 新しい割引タイプが追加されるたびに分岐が増える
throw new Error(`Unknown type: ${type}`);
}
ストラテジーで解決
type PricingStrategy = (base: number) => number;
const strategies: Record<string, PricingStrategy> = {
regular: (base) => base,
premium: (base) => base * 0.9,
vip: (base) => base * 0.8,
employee: (base) => base * 0.5,
};
function calculatePrice(type: string, base: number): number {
const strategy = strategies[type];
if (!strategy) throw new Error(`Unknown type: ${type}`);
return strategy(base);
}
// 新しい戦略の追加 = オブジェクトにプロパティを追加するだけ
実用例: ソート戦略
type SortStrategy<T> = (a: T, b: T) => number;
const byName: SortStrategy<User> = (a, b) => a.name.localeCompare(b.name);
const byAge: SortStrategy<User> = (a, b) => a.age - b.age;
const byCreatedAt: SortStrategy<User> = (a, b) => b.createdAt - a.createdAt;
// 実行時に切り替え
users.sort(byName);
users.sort(byAge);
実用例: 通知戦略
type NotifyStrategy = (message: string) => Promise<void>;
const emailNotify: NotifyStrategy = async (msg) => { await ses.send(msg); };
const slackNotify: NotifyStrategy = async (msg) => { await slack.post(msg); };
const snsNotify: NotifyStrategy = async (msg) => { await sns.publish(msg); };
// 環境変数で戦略を切り替え
const notify = strategies[process.env.NOTIFY_TYPE ?? 'email'];
await notify('Order completed');
ストラテジー vs if-else
| 観点 | if-else | ストラテジー |
|---|---|---|
| 拡張性 | 分岐を追加 | オブジェクトにプロパティを追加 |
| テスト | 全分岐をテスト | 各戦略を個別にテスト |
| 開放閉鎖原則 | 違反 (既存コードを変更) | 準拠 (新しい戦略を追加) |
さらに掘り下げるなら関連書籍が参考になる。