Strategyパターンとは？

この記事ではStrategyパターンについて学んだことをまとめます。使用言語はC#ですがJavaを使用されている方も理解できるはずです。まず開発における具体的な問題例を提示し、それをStrategyパターンで解決できることを説明していきます。初学者ですので、間違い等あればご指摘いただけると幸いです。

## この記事に向いている人

- オブジェクト指向プログラミング（特に継承とポリモーフィズムについて）を学んだが、抽象クラスやインターフェースの意義や使い方がよくわからない人。
- 以下に示す「問題例」のような問題にぶつかったことのある人。
- C#のデリゲート機能の意義がよくわからない人。

# 問題例

あなたはRPGゲームのキャラクターのふるまいを表すクラス群を作成している。それぞれのキャラは攻撃や防御、回復を行うことができるが、一部キャラは攻撃を行うことができない。また、それぞれのキャラは固有のスキルを持つ。

オブジェクト指向を学んだあなたは継承やポリモーフィズムを利用するため、以下のようにクラスを設計した（コンストラクタなどは省略）。

```csharp
// すべてのキャラクターの親クラス
abstract class Character
{
    public void Defence()
    {
			  // 防御行動
    }
    public void HealSelf(int amount)
    {
		    // 自己回復（アイテムなど）
    }
    // その他どのキャラも共通して有する行動のうち、処理まで共通のもの...

    abstract public void ActivateSkill(); // スキル使用。キャラクターによって内容は異なる
		// その他どのキャラも共通して有する行動のうち、処理がキャラにより異なるもの...
}

// 攻撃できるキャラが実装するインターフェース
interface Attackable // 
{
    public void Attack();
}

/**********************************************************************************/

// 以下、Characterの子クラス（各キャラのクラス）
class Hero : Character, Attackable // 勇者
{
    public override void ActivateSkill()
    {
        // スキル処理
    }

    public override void Attack()
    {
        // 攻撃処理
    }
}

class Warrior : Character, Attackable // 戦士
{
    public override void ActivateSkill()
    {
        // スキル処理
    }

    public override void Attack()
    {
        // 攻撃処理
    }
}

class Wizard : Character, Attackable // 魔法使い
{
    public override void ActivateSkill()
    {
        // スキル処理
    }

    public override void Attack()
    {
        // 攻撃処理
    }
}

class Priest : Character // 僧侶。ヒーラーなので攻撃できない
{
		public override void ActivateSkill()
    {
        // スキル処理
    }
}
```

ここで、以下のような欠陥が生じていることがわかります。

- 仮に勇者と戦士の攻撃処理が同じ場合、ふたつのクラスのAttackメソッドは同一になる。すなわち複数クラス間でコードが重複する。
- すべてのキャラに共通の行動以外の処理がすべて各クラスに直書きしてある。

これにより、キャラが増えてくると以下のような問題が生じます。

- 同じコードを何度も書く必要がある。
- それぞれのキャラの振る舞いを把握するために毎回コードを読む必要がある。
- 特定の攻撃処理に変更が生じた場合、その攻撃処理を行うクラスすべてをそれぞれ修正する必要がある。

したがって上の設計は開発効率の低下を引き起こす上、変更に著しく弱いといえるでしょう。

# 解決法

よく観察してみると上に示した問題の原因は、「攻撃処理はいくつかのキャラクターで共通しているのにもかかわらず、Character親クラスで抽象メソッドとして定義されていること」であるとわかります。このようにしてしまうと各キャラのクラス内にそれぞれ処理を直書きしなければならなくなるため、攻撃処理にパターンがあってもそれをまとめておけません。

それならば、「攻撃処理をキャラのクラスから分離して別の場所に閉じ込め、各キャラはそこに攻撃処理を移譲する」ようにすれば解決できるのではないでしょうか？Strategyパターンはこれの考え方に基づいています。Strategyパターンの定義を以下に示します。

## Strategy パターン

一連の処理をカプセル化し、交換できるようにするパターン。処理をメソッドとして直にクラスに設定するのではなく、インスタンス変数として設定したオブジェクトに処理を移譲（デリゲート）する。すなわち、そのオブジェクトのメソッドを呼び出すことで処理を実行する。

- したがって、そのインスタンス変数は必ず特定のメソッドを持つ必要がある。インターフェースや抽象クラスを利用する。
- また、インスタンス変数として設定することで動的に処理を変更することが可能になる。

上のコードをStrategyパターンを使って書き直してみましょう。

```csharp
abstract class Character
{
    public AttackAction attackAction; // AttackAction設定用変数

    public void Attack()
    {
        attackAction.DoAttack(); // 攻撃行動の処理をattackActionに委譲（デリゲート）
    }
    
    public void Defence()
    {
        // 防御行動    
    }
    
    public void HealSelf(int amount)
    {
        // 自己回復（アイテムなど）
    }
    // その他どのキャラクターも共通して有する行動のうち、処理まで共通のもの...

    abstract public void ActivateSkill(); // スキル使用。キャラクターによって内容は異なる
		// その他どのキャラクターも共通して有する行動のうち、処理がキャラにより異なるもの...
}

/**********************************************************************************/
//>
// 各攻撃方法クラスが実装するインターフェース（抽象クラスでもOK）
interface AttackAction
{
    public void DoAttack();
}

class Slash : AttackAction // 勇者および戦士の攻撃
{
    public void DoAttack()
    {
        // 剣で切る処理
    }
}

class CastSpell : AttackAction // 魔法使いの攻撃
{
    public void DoAttack()
    {
        // 呪文攻撃の処理
    }
}

class NoAttack : AttackAction // 僧侶の攻撃（何もしない）
{
    public void DoAttack()
    {
        // 何もしない
    }
}
//<
/**********************************************************************************/

// 以下、Characterの子クラス（各キャラのクラス）
class Hero : Character // 勇者
{
    public Hero()
    {
        this.attackAction = new Slash(); // 攻撃方法の設定
    }

    public override void ActivateSkill()
    {
        // スキル内容...
    }
}

class Warrior : Character // 戦士
{
    public Warrior()
    {
        this.attackAction = new Slash(); // 攻撃方法の設定
    }

    public override void ActivateSkill()
    {
        // スキル内容...
    }
}

class Wizard : Character // 魔法使い
{
    public Character()
    {
        this.attackAction = new CastSpell(); // 攻撃方法の設定
    }

    public override void ActivateSkill()
    {
        // スキル内容...
    }
}

class Priest : Character // 僧侶。攻撃できない
{
    public Priest()
    {
        this.attackAction = new NoAttack(); // 攻撃できないように設定
    }

		public override void ActivateSkill()
    {
        // スキル内容...
    }
}
```

攻撃処理を別のクラスに分離して閉じ込め（カプセル化）、そのクラスのインスタンスを保持するようにすることでコードの重複をうまく回避することができました。各キャラクラスの攻撃処理も、コンストラクタを見ればパターンが一瞬で理解できます。攻撃処理の変更は各攻撃方法クラスを変更するだけで済むようになりました。

また、このリファクタリングによって更なる効果を得られたようです。attackActionの中身を動的に変更することで、動的に攻撃処理を変更することができます。例えば「前のターンに敵が行った攻撃を行う」というようなキャラも簡単に実装することができるようになりました。

## 補足

以下はC#をお使いの方でdelegateがどのようなものか知っている人向けになります。もうお気づきかもしれませんが、delegateキーワードを使うことで攻撃方法クラスを定義する代わりに攻撃方法メソッドを定義することができます。これによりファイル数を削減できます。上のファイルの `//>` と `//<` に囲まれた部分を以下のコードに変更し、各キャラクラスのコンストラクタで設定する値をnew式の代わりに `AttackActions.{メソッド名}` とすればOKです。

```csharp
public delegate void AttackAction(); // デリゲート定義
/*
 delegate キーワードは "メソッド型" を定義するキーワード。
 AttackAction という名前の引数と戻り値のないメソッドの型を作成した。
 今回はメソッドを static にしたため、キャラクラスのインスタンス変数の参照や変更が必要なら引数や 戻り値を設定するべき。
 例えば攻撃力 (int) を参照するなら delegate void AttackAction(int); として呼び出し側で攻撃力を渡せばよい。
*/

public class AttackActions
{
    public static void Slash()
    {
        // 剣で切る処理
    }

    public static void CastSpell()
    {
        // 呪文攻撃の処理
    }

    public static void NoAttack()
    {
        // 何もしない
    }
}
```