注)以下の内容は「オブジェクト指向における再利用のためのデザインパターン 改訂版」(ソフトバンク パブリッシング 株式会社発行 著者:Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides 監訳者:本位田 真一、吉田 和樹)からの引用を多分に含んでいます。
  C++ でプログラミングをする上でのレベルアップには欠かせない名著です。
  興味を持たれた方は是非ご購入をお勧めします。


Strategyパターン

 各アルゴリズムのカプセル化によりそれらを交換・拡張可能にする。

[動機]
 ・クライアントが複数のアルゴリズムを保持すると、クライアントが大きくなり
  複雑になる。

 ・アルゴリズムは使い分けが必要で、複数のアルゴリズムを持つことが必要と
  される場合はすくなくない。

 ・採用されているアルゴリズムが重要であればあるほど、新しいアルゴリズムの
  追加や、アルゴリズムの変更が困難になる。
[適用可能性]
 ・多くの似たクラスにおいて振る舞いのみが異なっている場合
  そのクラスを複数作ることなく、アルゴリズムの差し替えで対応できる。

 ・複数のアルゴリズムを必要とする場合

 ・アルゴリズムがクライアントが知るべきでないデータを利用している場合
  複雑でアルゴリズム特有なデータ構造を公開するのを避けることができる

 ・クライアントが多くの振る舞いを定義していて、これらがオペレーション内で
  複数の条件文として現れている場合
  条件分岐後の処理をStrategyクラスに移し換える
[構成要素]

Contextクラス …… Context:情勢・情況
 ConcreteStrategyオブジェクトを備えている。
 Strategyのオブジェクトに対する参照を保持する。
 StrategyクラスがContextクラスのデータにアクセスする為の
インターフェイスを備えても良い

Strategyクラス …… Strategy:戦略
 サポートする全てのアルゴリズムに共通のインターフェイスを宣言する。
ContextクラスはConcreteStrategyクラスにより定義されるアルゴリズムを
呼び出すためにこのインターフェイスを利用する

ConcreteStrategyクラス(A/B/C/...) …… Concrete:具体的な
 Strategyクラスのインターフェイスを利用して、アルゴリズムを実装する
[構造]
      集約関係(ポインタ・参照などで実装)
Contextクラス ◇--------------> Strategyクラス
|
| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄| ̄ …
ConcreteStrategyAクラス ConcreteStrategyBクラス ConcreteStrategyCクラス
[協調関係]

 ・ContextとStrategy
  Contextオブジェクトはアルゴリズムに必要な全てのデータをStrategy
 オブジェクトに対して送る。
  ContextオブジェクトはStrategyクラスのオペレーションの引数として
 自身を渡すこともできる。

  Contextオブジェクトはクライアントからの要求をStrategyオブジェクト
 に送る。クライアントはConcreteStragetyオブジェクトを生成し、これを
 Contextに渡す。その後クライアントはContextオブジェクトだけとやりとり
 をする。
[結果]
長所
 ・アルゴリズムや振る舞いの集合がStrategyクラスの階層により定義されている
  ため、Contextオブジェクトで再利用が可能。

 ・Strategyのサブクラスに別々にアルゴリズムをカプセル化することで
  Contextオブジェクトとは独立にアルゴリズムを変更することができる。
  すなわち、「アルゴリズムの切り替え・拡張を容易にする」

 ・異なる振る舞いを一つのクラスにまとめた場合、正しい振る舞いを選択するために
  条件文を利用せざるを得ない。ところが、異なる振る舞いをStrategyの別々の
  クラスにカプセル化することにより、これらの条件文が排除される。

<Strategyパターンを使用しない場合>
  type = ENEMY_WEAKEST;

  switch(type)
  {
   case ENEMY_WEAKEST:
    ActEnemyWeakest();
    break;

   case ENEMY_STRONGEST:
    ActEnemyWeakest();
    break;
  }

<Strategyパターンを使用した場合>
  enemy-> new Enemy( new StrategyEnemyWeakest );
            //new StrategyEnemyStrongest;

  enemy->Action();
(欠点)
 ・クライアントがConcreteStrategyパターンを選択する前に、それぞれの
  ConcreteStrategyパターンがどのように異なっているか知る必要がある。
  また、新しいStrategyが追加されたときも、そのことをクライアントに
  教える必要がある。

 ・ConcreteStrategyの採用しているアルゴリズムによってはインターフェイスを
  通して送られるすべての情報を利用しないことは十分にあり得る。
  これは利用することのないパラメータをContextクラスが生成し、初期化する
  ことを意味している。もしこれが問題ならば、StrategyクラスとContextクラス
  の間の密な結合が必要になる。

 ・ConcreteStrategyを複数生成することによりオブジェクト数が増加する。
  もし複数のContextオブジェクトが共通するConcreteStrategyを利用する
  のであれば、それらを共有することでオブジェクト数の増加を抑えることが
  できる可能性がある。
  もしStrategyが状態を必要とするのであれば、Contextオブジェクトに
  情報を保持させ、ConcreteStrategyへ要求を出す際にコレを一緒に渡す。
  (共有されるConcreateStrategyオブジェクトに状態を共有化できたとしても
   そのような状態を持たせるべきではない。Flyweightパターンより)
[実装]
 ・StrategyクラスとContextクラスのインターフェイス定義。

  Contextオブジェクトのどのようなデータに対しても、Contextクラスの
 インターフェイスは、ConcreateStrategyオブジェクトが効果的にアクセス
 できるようにしなければならない。

 コレを実現するには3つ方法が考えられる。

 1. Strategyクラスにおける関数の引数を使ってContextクラスがデータを渡す。
   という手法。
   言い換えれば「ConcreteStrategyオブジェクトにデータを持っていく。」

   これによりStrategyクラスとContextクラスを未結合の状態に保つことが
   できる。
   しかし、Contextクラスが必要ないデータまで送る可能性がある。


 2. Contextオブジェクトに引数として自身を送らせ、コレを受け取った
   ConcreteStrategyオブジェクトがそのContextオブジェクトに対して
   データを明示的に要求する。
   という手法。


 3. StrategyのオブジェクトにContextオブジェクトに対する参照を持たせて、
   どのような情報の受け渡しも必要がないようにする。
   という手法。

  2 と 3 のどちらもStrategyオブジェクトは必要な情報だけを要求できる
 ようになる。
  しかし、このためにはContextクラスはデータに対する複雑なインターフェイス
 を提供しなくてはならなくなり、これによりStrategyクラスとContextクラスは
 より密に結合することになる。



 ・テンプレートパラメータとしてStrategyクラスを持たせる。

 テンプレートパラメータとしてStrategyクラスを利用することで、Strategyクラスを
 Contextクラスに静的に結合することができ、これにより実行効率を上げることが
 できる。

 ただし、コレは「Strategyクラスをコンパイル時に決定できる」「Strategyクラスを
 実行時に変更する必要がない」ことが条件となる。

 template <class AStrategy>
 class Enemy
 {
   void Action(){ theStrategy.DoAlgorithm(); //...};
  //...
  private:
   AStrategy theStrategy;
 };

 このようなクラスをインスタンス化する際に、パラメータとしてStrategyクラスが
 与えられる。

 class StrategyEnemyWeakest
 {
  public:
   void DoAlgorithm();
 };

 Enemy<StrategyEnemyWeakest> aEnemy;
サンプルコード
 ここでは実際に私が開発している「ぐるぐる大回転」に使用しようとしているモノ
をサンプルコードとして紹介する。
 簡単にするために「ぐるぐる大回転」=「一般的な落ちモノ(ぷよぷよ)」と
考えていただいて構わない。

 まず登場するクラスについて説明する。

 MainStageクラス
  ・対戦が行われるメインステージ
  ・プレイヤーの個数だけFieldクラスを保持する
  ・ゲーム終了の判定。
  ・キーボードからの入力を判定する。
  ・各フィールドに対して計算・描画を指示。

 Fieldクラス
  ・各プレイヤーに与えられるフィールド
  ・2人対戦の場合はFieldが二つ存在する
  ・ゲーム内の具体的な計算。
  ・FallingGroupクラスの移動処理。
  ・FallinObjectMapに登録されたFallingObjectに描画指示。

 FallingObjectクラス
  ・落下してくる落ちモノのオブジェクト一つずつ
  ・コレを複数個備えたモノがFallingGroupクラス
  ・自身を描画する。

 FallingGroupクラス
  ・FallingObjectインスタンスを複数持つ
  ・所持しているFallingObjectインスタンスへの描画指示。
  ・回転・移動などの処理。


以上が今回サンプルとして紹介する私のプログラムの主要クラスである。

 ココに今回は

 Enemyクラス
  ・Fieldクラスが持っているFallingGroupクラスのインスタンスを
   操作する
  ・Strategyクラスのへのポインタをメンバ変数として持つ
  ・Strategyにより操作内容を決定する
  ・プレイヤーが操作しているフィールドには不要
  ・プレイヤー VS プレイヤーの二人対戦の時は不要


 StrategyEnemyクラス(抽象クラス)
  ・FallingGroupクラスをどのように動かすのか決定する

  StrategyEnemyRandomクラス(ConcreteStrategyクラス)
   ・全てを乱数により決定する

  StrategyEnemySimulateクラス(ConcreteStrategyクラス)
   ・全てをシミュレートし、最善の手段を選ぶ

というクラスを組み込む。

 プレイヤー数を管理しているのはMainStageクラスであるから
Enemyクラスのインスタンスの生成の制御はMainStageクラスが
行う。
 そこでPersonクラスという抽象クラスを作成し、そこからPlayerクラスと
Enemyクラスを派生させる。
PersonクラスはFieldクラスのインスタンスを持ち、Playerクラスは
キーボード入力を処理しFallingGroupを制御し、Enemyクラスは思考のための
Strategyクラスをもち、StrategyEnemyクラスのサブクラスにより
行動を決定し、FallingGroupを制御することにする。
 StageMainインスタンスはPersonクラスへのポインタを持ち、それを
通じて処理を行うため、実際にはどちらを制御しているのか知る必要は
ない。


class Person  // 抽象クラスとして生成する
{
public:
 virtual bool onMotion()=0;  // 純粋仮想関数
// ...
};

class Player :public Person  // プレイヤー用
{
public:
 virtual bool onMotion();  // ポリモルフィズム対応
// ...
};

class Enemy  // NPC用
{
public:
 virtual bool onMotion();  // ポリモルフィズム対応
// ...
};


bool Player::onMotion()  // プレイヤーは入力受付
{
 GetInput();
 // ...
}

bool Enemy::onMotion()  // NPCは計算
{
 GetAction();
 // ...
}


bool StageMain::onMotion()  // StageMainはどちらか知る必要なし
{
 for(num=0; num<PLAYER_MAX; ++)
  p_person[num]->onMotion();
 // ...
}



EnemyStrategyにより行動を決定するクラスがEnemyクラスである。

class Enemy
{
public:
 Enemy(StrategyEnemy*);
 ~Enemy(){ delete m_strategy;};
 virtual bool onMotion();
 void GetAction();    // 行動決定
private:
 StrategyEnemy* m_strategy;
 FallingObject* FallingObjectMap[FIELD_WIDTH][FIELD_HEIGHT];
 FallingGroup*  mp_falling_group;

 ACT m_action[ACT_MAX];
};

 個々のConcreateStrategyの親となるはStrategyEnemyであり抽象クラス
として実装する。

class StrategyEnemy  // 抽象クラス
{
public:
 virtual void DecideAction( ACT& action,
               const FallingObject** const pFallingObjectMap,
               const FallingGroup* p_falling_group
              ) = 0;
protected:
 StrategyEnemy();
};

 実際に行動を決定する動作は以下のようにポリモルフィズムを利用して
行われる。

void Enemy::GetAction()
{
 m_strategy->DecideAction(m_action, pFallingObjectMap, mp_falling_group);
}


 ここでStrategyEnemyのサブクラスを考える。StrategyEnemyRandomは行動の
全てを乱数によって決定する。

class StrategyEnemyRandom :public StrategyEnemy
{
public:
 StrategyEnemyRandom();

 virtual void DecideAction( ACT& action,
               const FallingObject** const pFallingObjectMap,
               const FallingGroup* p_falling_group
              );
             // ...
};

StrategyEnemySimulationは行動を全てをシミュレートし決定する。

class StrategyEnemySimulation :public StrategyEnemy
{
public:
 StrategyEnemySimulation();

 virtual void DecideAction( ACT& action,
               const FallingObject** const pFallingObjectMap,
               const FallingGroup* p_falling_group
              );
             // ...
};

 StrategyEnemySimulationクラスは与えられた情報を有効に使うのに対し
StrategyEnemyRandomクラスは与えられた情報を全て無視し、乱数により
行動を決定する。


 Enemyクラスをインスタンス化する際に利用したいStrategyEnemyクラスの
オブジェクトを渡すようにする。


 Enemy* enemy_random = new Enemy( new StrategyEnemyRandom );
 Enemy* enemy_simulation = new Enemy( new StrategyEnemyRandom );


 StrategyEnemyクラスのインターフェイスは、そのサブクラスが実装するであろう
全てのアルゴリズムをサポートするために、注意深く設計する必要がある。
新しいアルゴリズムを導入するたびに、このインターフェイスを変更しなければ
ならなくなるのは望ましくない。それは既存のサブクラスの変更を意味する。


 一般に、StrategyクラスとContextクラスのインターフェイスにより、このパターンが
どれだけ目標を達成できるかが決まる。