デザインパターン ~Strategy~

公開:2020/10/20
更新:2020/10/20
5 min読了の目安(約4500字TECH技術記事

0. はじめに

GoFのデザインパターンにおける、Strategyパターンについてまとめます。

1. Strategyパターンとは

  • Strategyという英単語は、戦略という意味になります。プログラミングの場合はアルゴリズムと考えていいみたいです。
  • どんなプログラムも問題を解くために書かれています。問題を解くために特定のアルゴリズムが実装されています。Strategyパターンは、アルゴリズムを実装した部分をごっそりと交換できる方式です。
  • GoFのデザインパターンでは、振る舞いに関するデザインパターンに分類されます。

2. サンプルクラス図

Strategy.PNG

3. サンプルプログラム

じゃんけんを行うプログラムです。ランダムに手を出す戦略とグーのみの手を出す戦略があります。

3-1. Handクラス

じゃんけんの手を表すクラスです。

Hand.java
public class Hand {

	public static final int HANDVALUE_GUU = 0;
	public static final int HANDVALUE_CHO = 1;
	public static final int HANDVALUE_PAA = 2;

	public static final Hand[] hand = {
			new Hand(HANDVALUE_GUU),
			new Hand(HANDVALUE_CHO),
			new Hand(HANDVALUE_PAA),
	};

	private int handvalue;

	private Hand(int handvalue) {
		this.handvalue = handvalue;
	}

	public static Hand getHand(int handvalue) {
		return hand[handvalue];
	}

	public boolean isStrongerThan(Hand h) {
		// thisがhより強いときtrue
		return fight(h) == 1;
	}

	private int fight(Hand h) {
		if (this == h) {
			// 引き分け
			return 0;
		} else if ((this.handvalue + 1) % 3 == h.handvalue) {
			// thisの勝ち
			return 1;
		} else {
			// hの勝ち
			return -1;
		}
	}
}

3-2. Playerクラス

じゃんけんを行うプレイヤーを表すクラスです。

Player.java
public class Player {

	private String name;
	private Strategy strategy;
	private int wincount;
	private int losecount;
	private int gamecount;

	public Player(String name, Strategy strategy) {
		this.name = name;
		this.strategy = strategy;
	}

	public String getName() {
		return name;
	}

	public Hand nextHand() {
		return strategy.nextHand();
	}

	public void win() {
		wincount++;
		gamecount++;
	}

	public void lose() {
		losecount++;
		gamecount++;
	}

	public void even() {
		gamecount++;
	}

	public String toString() {
		return "[" + name + "] " + gamecount + " gemes, " + wincount + " win, " + losecount + " lose";
	}
}

3-3. Strategyインターフェース

じゃんけんの「戦略」を表すインターフェースです。

Strategy.java
public interface Strategy {
	public abstract Hand nextHand();
}

3-4. RandomStrategyクラス

ランダムに手を出す戦略を表すクラスです。

RandomStrategy.java
import java.util.Random;

public class RandomStrategy implements Strategy {

	public Hand nextHand() {
		Random random = new Random();
		return Hand.getHand(random.nextInt(3));
	}
}

3-5. GuuStrategyクラス

グーのみの手を出す戦略を表すクラスです。

GuuStrategy.java
public class GuuStrategy implements Strategy {

	public Hand nextHand() {
		return Hand.getHand(Hand.HANDVALUE_GUU);
	}
}

3-6. Mainクラス

メイン処理を行うクラスです。

Main.java
public class Main {

	public static void main(String[] args) {

		Player player1 = new Player("Taro", new RandomStrategy());
		Player player2 = new Player("Hana", new GuuStrategy());
		for (int i = 0; i < 5; i++) {
			Hand nextHand1 = player1.nextHand();
			Hand nextHand2 = player2.nextHand();
			if (nextHand1.isStrongerThan(nextHand2)) {
				System.out.println("Winner:" + player1.getName());
				player1.win();
				player2.lose();
			} else if (nextHand2.isStrongerThan(nextHand1)) {
				System.out.println("Winner:" + player2.getName());
				player1.lose();
				player2.win();
			} else {
				System.out.println("Even...");
				player1.even();
				player2.even();
			}
		}

		System.out.println("----- Total result -----");
		System.out.println(player1.toString());
		System.out.println(player2.toString());
	}
}

3-7. 実行結果

Winner:Taro
Winner:Hana
Even...
Winner:Hana
Even...
----- Total result -----
[Taro] 5 gemes, 1 win, 2 lose
[Hana] 5 gemes, 2 win, 1 lose

4. メリット

Strategyパターンでは、アルゴリズムの部分をほかの部分と意識的に分離します。そしてアルゴリズムとのインターフェースの部分だけを規定し、委譲によってアルゴリズムを利用します。
これは、プログラムを複雑にしているように見えますが、そうではありません。例えば、アルゴリズムを改良してもっと高速にしたい場合、Strategyパターンを使っていれば、Strategy役のインターフェースを変更しないようにして、アルゴリズムをだけを追加、修正すればいいのです。委譲というゆるやかな結びつきを使っているのため、アルゴリズムを用意に切り替えることができます。
また、ゲームプログラム等では、ユーザーの選択に合わせて難易度を切り替えたりすることにも使えます。

5. GitHub

6. デザインパターン一覧

7. 参考

今回の記事、及びサンプルプログラムは、以下の書籍を元に作成させて頂きました。

大変分かりやすく、勉強になりました。感謝申し上げます。
デザインパターンやサンプルプログラムについての説明が詳細に書かれていますので、是非書籍の方もご覧ください。