【Java】Strategyパターンで状況に応じて処理を変える


デザインパターンのうちのひとつ、Strategyパターンについて学んだのでまとめます。

Strategyパターンとは

StrategyパターンとはGoFのデザインパターンのひとつです。

Strategyとは「戦略」のことであり、
Strategyパターンとは状況に応じてオブジェクトの振る舞いを変えるための設計パターンです。

Strategyパターンのメリット

  • 処理を分離することでメンテナンスのしやすさがアップ
  • 新たな戦略を追加したい場合はStrategyインターフェースを継承したクラスを追加するだけですむ
  • 処理をカプセル化することで呼び出し側が具体的な処理内容を意識せずに利用することが可能

実際に使ってみる

今回はポケモンのピカチュウを例として、状況に応じて技(戦略、振る舞い)を変えてみます。

Strategyパターンではそれぞれの戦略が共通したインターフェースを持つ必要があります。

下記にAtackStrategyインターフェースを用意します。

AttackStrategy.java
/**
* 攻撃インターフェース
*/
public interface AttackStrategy {
    public void attack();
}

そして、戦略ごとに上記インターフェースを実装したクラスを用意します。

インターフェースに記述したメソッドをオーバライドして実際の処理を記述します。

今回は標準出力のみとしますが、もっと複雑な処理を記述することも可能です。

ToGoodTypeConcreteStrategy.java
/**
* 敵が得意属性の場合の戦略
*/
public class ToGoodTypeConcreteStrategy implements AttackStrategy {

    @Override
    public void attack() {
        System.out.println("10万ボルトだ!");
    }
}
ToBadTypeConcreteStrategy.java
/**
* 敵が苦手属性の場合の戦略
*/
public class ToBadTypeConcreteStrategy implements AttackStrategy {

    @Override
    public void attack() {
        System.out.println("影分身だ!");
    }
}

続いて、上記で用意したAttackStrategyインターフェースを型とする変数_attackStrategyをフィールドに持つPikachuクラスを用意します。

Pikachuクラスには_attackStrategyを初期化するためのコンストラクタやセッターを定義します。

また、attack()を定義し、AttackStrategy#attack()を呼び出すようにします。(委譲)

ここがStrategyパターンのポイントで、PikachuContexクラスにはattack()しか定義していないにも関わらず、状況に合わせて実際の処理内容を変更できるようになります。

PikachuContext.java
public class PikachuContext {
    private AttackStrategy _attackStrategy;

    public Pikachu(AttackStrategy strategy) {
        setStrategy(strategy);
    }

    public void attack() {
        _attackStrategy.attack();
    }
}

以上で準備完了です。
実際に使用してみます。

Main.java
public class Main {

    public static void main(String[] args) {

        PikachuContext pikachu;

        // 敵が得意属性の場合
        pikachu= new PikachuContext(new ToGoodTypeConcreteStrategy());
        pikachu.attack(); // "10万ボルトだ!" が出力される

        // 敵が苦手属性の場合
        pikachu = new PikachuContext(new ToBadTypeConcreteStrategy());
        pikachu.attack(); // "影分身だ!" が出力される
    }
}