DPDesign Pattern Quest
#21 / 23
B振る舞いDIFF

ストラテジー

Strategy

ナビアプリの『徒歩 / 車 / 電車』切替。同じ目的地に対し、別のアルゴリズムを実行時に差し込む。

Intent · 目的

目的は同じだけれど解き方が複数あるアルゴリズム群を、共通I/F の下にカプセル化して、利用側から1つ選んで差し込めるようにする。

Motivation · 動機

ECサイトの価格計算。一般会員は定価、メンバーは10%引き、VIPは30%引き、年末セール期間は別ロジック…と、`if 会員種別 == ...` を計算メソッドの中に書き始めると、ロジックが膨らむほど読めなくなる。Strategy なら『価格計算の戦略』を1つの抽象として切り出して、Cart は今アクティブな戦略に「計算して」と頼むだけ。新しい割引ロジックを足したい? クラス(または関数)を1個追加するだけ ── Cart には触らない。

適用場面

  • 1同じ目的に対し、複数の解法・実装方式を実行時に切り替えたい場合。
  • 2if/switch でアルゴリズムを選んでいる箇所を、もっと綺麗にしたい場合。
  • 3テストでアルゴリズムを差し替えたい(モックや決定論的な実装にしたい)。

概念図

サンプルコード

type PricingStrategy = (base: number) => number;

const regular: PricingStrategy = b => b;
const member:  PricingStrategy = b => Math.floor(b * 0.9);
const vip:     PricingStrategy = b => Math.floor(b * 0.7);

class Cart {
  constructor(private strategy: PricingStrategy = regular) {}
  setStrategy(s: PricingStrategy){ this.strategy = s; }
  total(base: number){ return this.strategy(base); }
}

const c = new Cart();
c.setStrategy(vip);
console.log(c.total(1000)); // 700
Pros · メリット
  • アルゴリズムの追加・差し替えが容易で、本体クラスは触らない。
  • アルゴリズム1個ずつテストできる。
  • if/switch の塊が消え、読みやすい平らなコードになる。
Cons · デメリット
  • アルゴリズムが2〜3種類しかなく、増える予定もないなら、ただの遠回り。
  • クライアントがどの戦略を使うか知っている必要がある(普通は DI やファクトリで補完)。
  • 戦略間で『同じ前処理』が必要な場合、どこに置くか悩ましい。

関連パターン

⚔ ready to test

理解度を確かめる時間だ

3 問のクイズで、 このパターンを身体に染み込ませよう。

挑戦する →