勉強日記

チラ裏

GoF本 Strategy

ねらい

AKA

  • Policy

つかいどころ

  • ふるまいだけが異なる似通ったクラスが複数ある
  • いくつかのアルゴリズムを選べるようにしたい
  • アルゴリズム固有のデータを隠蔽したい
  • 同じような分岐がいくつものメソッドにある
    • enumか何かで分岐している
    • enumに対応するStrategyを用意して、これを排することができる

構造

オブジェクトの集約版

f:id:wand_ta:20190102181104p:plain

ジェネリクス

f:id:wand_ta:20190102181116p:plain

  • 制限付き
  • 静的にバインドするので実行時効率が良い

登場人物

  • Strategy
  • Concretestrategy
    • Strategyの実装クラス
    • アルゴリズム固有のデータを保持することも
    • 状態をもたなければ、FlyweightやSingletonにできる
  • Context
    • concretestrategyオブジェクトでカスタムできる
    • strategyオブジェクトを保持
      • nullableにして、デフォルト動作をContextに実装する方法もある
    • Strategyからデータアクセスするためのインターフェースを設けることも

クライアントコードからの利用

  • クライアントはconcretestrategyオブジェクトを選択・生成し、contextに渡す
  • 以降、concretestrategyオブジェクトを直接触ることはない
  • contextconcretestrategyオブジェクトに処理を委譲する
    • 必要なデータだけ渡すパターン
    • context丸ごと渡して、concretestrategy側で必要なものを取得するパターン

結果

  • 関連するアルゴリズムが1つのクラスツリーに切り出される
    • ベースクラスに共通の処理を置くことができる
      • 【補】Template Method Pattern
  • 継承ではないアプローチである
    • Contextを継承するデメリット
      • 振る舞いだけ異なるContext派生が大量に生まれる
      • Contextの実装とアルゴリズムの実装とが混然一体となり、変更・拡張が大変になる
        • 【補】「実装は問わず、とりあえずソートさえできればいい」場合
          • qsort(collection)mergesort(collection)とかが並ぶと見通しが悪い
          • sortStrategy->sort(collection); と抽象的に書けたほうが見通しが良い
      • オブジェクト生成後にアルゴリズムを動的に変更できない
  • 条件分岐がなくなる
    • Contextの中でenumアルゴリズムを分岐していたらStrategy Patternを適用すべきサイン
      • Strategy Patternを知らない人が、
      • Contextの継承も行わずに、
      • 複数のアルゴリズムを選択可能にするとこうなる
  • ブラックボックスの実装を選択可能にできる
  • クライアントはStrategyのバリエーションを知っている必要がある
    • 適切なものを選択するために、その中身についても知っている必要がある
    • 【補】ソートアルゴリズム
      • 素数がxx以下なら、O(n^2)でもオーバヘッドがない分バブルソートのほうが効率が良い、とか
  • StrategyContextの相互作用のオーバヘッド
    • Concretestrategyごとに必要なパラメータが異なることがある
    • Strategyのインターフェースは和集合的でなければならない
    • したがって、必要ない引数を生成し渡してしまうケースが出てくる
    • ConcretestrategyContextとを密結合にすることで回避可能
  • オブジェクトの数が増える
    • Concretestrategyに状態を持たせず、SingletonやFlyweightにすることで軽減可能

実装にあたり考えるべきこと

  • ContextStrategyのインターフェース
    • ConcretestrategyContextから必要なデータを取得できる必要がある
      • 引数で渡す(push)
        • Concretestrategyで必要なものの和集合になる
        • ので、無駄が生じうる
      • contextを丸ごと渡し、Concretestrategyで必要なものを取得する(pull)
        • 無駄はなくなる
        • ContextConcretestrategyとが密結合してしまう
  • StrategyContext<Strategy>のテンプレート型引数にする
  • strategyオブジェクトをオプショナルにする
    • なければデフォルト動作、あれば処理を委譲
    • 【所感】nullチェックが鬱陶しいのでDefaultStrategyを用意したほうが良くないですか(Null Object Pattern)

関連するパターン

  • Flyweight
  • Singleton
    • ともに、Strategyが状態をもたなければ適用可能

英語

  • factor out
    • 因数分解する
    • 構成要素を取り除く
      • 長くなりすぎた関数の一部を外に追い出すときなど
      • refactorの一つの手段として factoring out がある