Strategyパターン

###Strategyパターンとは
どんなプログラムも問題を解くために書かれている。
そして問題を解くために特定のアルゴリズムが実装されている。
Strategyパターンでは、そのアルゴリズムを実装した部分が交換できるようになっている。
アルゴリズムを切り替え、同じ問題を別の方法で解くのを容易にするパターン。

###Strategy(戦略)の役
戦略を利用するためのインターフェースを定める役。

````java
package strategy;

public interface Strategy {
	// 次回出す手を決定する
	public abstract Hand nexHand();
	// 前回出した手で勝ったかどうかを学習する
	public abstract void study(boolean win);
}
````

###ConcreteStrategy(具体的戦略)の役
Strategy役のインターフェースを実際に実装する役。
ここで具体的に戦略(作戦、方法、方策、アルゴリズム)を実際にプログラミングする。

````java
package strategy;

import java.util.Random;

public class WinningStrategy implements Strategy{
	private Random random;
	private boolean won = false;
	private Hand prevHand;

	public WinningStrategy(int seed) {
		random = new Random(seed);
	}

	/**
	 * 前回勝っていれば同じ手を出し、負けていればランダムで手を決定する
	 */
	@Override
	public Hand nextHand() {
		if (!won) {
			prevHand = Hand.getHand(random.nextInt(3));
		}
		return prevHand;
	}

	@Override
	public void study(boolean win) {
		won = win;
	}
}
````

````java
package strategy;

import java.util.Random;

public class ProbStrategy implements Strategy {
	private Random random;
	private int prevHandValue = 0;
	private int currentHandValue = 0;
	// 過去の勝敗を反映した確率計算のための表
	//[前回に出した手][今回出す手]
	private int[][] history = {
			{1,1,1,},
			{1,1,1,},
			{1,1,1,},
	};

	public ProbStrategy(int seed) {
		random = new Random(seed);
	}

	/**
	 * historyの値から確率で計算を行う
	 * 例えば、history[0][0]の値が3、history[0][1]の値が5、history[0][2]の値が7の場合
	 * グー、チョキ、パーを出す割合を3:5:7として次の手を決定する。
	 * 0以上15未満(15は3+5+7の合計値)の乱数値を得て、
	 * 0以上3未満ならグー
	 * 3以上8未満ならチョキ
	 * 8以上15未満ならパー
	 * とする
	 */
	@Override
	public Hand nextHand() {
		int bet = random.nextInt(getSum(currentHandValue));
		int handValue = 0;
		if (bet < history[currentHandValue][0]) {
			handValue = 0;
		} else if (bet < history[currentHandValue][0] + history[currentHandValue][1]) {
			handValue = 1;
		} else {
			handValue = 2;
		}

		prevHandValue = currentHandValue;
		currentHandValue = handValue;
		return Hand.getHand(handValue);
	}

	private int getSum(int hv) {
		int sum = 0;
		for (int i = 0; i < 3; i++) {
			sum += history[hv][i];
		}
		return sum;
	}

	/**
	 * nextHandメソッドで返した手の勝敗を元に、historyフィールドの内容を更新する
	 *
	 */
	@Override
	public void study(boolean win) {
		if (win) {
			history[prevHandValue][currentHandValue]++;
		} else {
			history[prevHandValue][(currentHandValue + 1) % 3]++;
			history[prevHandValue][(currentHandValue + 2) % 3]++;
		}
	}
}
````
###Context(文脈)の役
Strategy役を利用する役。
ConcreateStrategy役のインスタンスを持っていて、必要に応じてそれを利用する。あくまで呼び出すのはStrategy役のインターフェース。

````java
package strategy;

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 Hand nextHand() {
		// 実際に次の手を決定するのは自分の「戦略」
		return strategy.nextHand();
	}

	public void win() {
		// 戦略の内部状態を変化させる
		strategy.study(true);
		winCount++;
		gameCount++;
	}

	public void lose() {
		// 戦略の内部状態を変化させる
		strategy.study(false);
		loseCount++;
		gameCount++;
	}

	public void even() {
		gameCount++;
	}

	public String toString() {
		return "[" + name + ":" + gameCount + " games, " + winCount + " win, " +
	loseCount + " lose" + "]";
	}
}
````

###呼び出し元

````java
package strategy;

public class Main {
	public static void main(String[] args) {
		if (args.length != 2) {
			System.out.println("Usage: java Main randmeseed1 randomeseed2");
			System.out.println("Example: java Main 314 15");
			System.exit(0);
		}

		int seed1 = Integer.parseInt(args[0]);
		int seed2 = Integer.parseInt(args[1]);
		Player p1 = new Player("Jiro", new WinningStrategy(seed1));
		Player p2 = new Player("Taro", new ProbStrategy(seed2));
		for (int i = 0; i < 10000; i++) {
			Hand nexHand1 = p1.nextHand();
			Hand nexHand2 = p2.nextHand();
			if (nexHand1.isStrongerThan(nexHand2)) {
				System.out.println("Winner:" + p1);
				p1.win();
				p2.lose();
			} else if (nexHand2.isStrongerThan(nexHand1)) {
				System.out.println("Winner:" + p2);
				p1.lose();
				p2.win();
			} else {
				System.out.println("Even…");
				p1.even();
				p2.even();
			}
		}

		System.out.println("Total result:");
		System.out.println(p1);
		System.out.println(p2);

	}
}
````

###実行結果

![スクリーンショット 2020-09-08 13.43.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197834/39bd1678-2ff3-af3a-9366-5429b0b962b2.png)

https://github.com/aki0207/strategy

こちらを参考にさせていただきました。
<a href="https://www.amazon.co.jp/%E5%A2%97%E8%A3%9C%E6%94%B9%E8%A8%82%E7%89%88Java%E8%A8%80%E8%AA%9E%E3%81%A7%E5%AD%A6%E3%81%B6%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E5%85%A5%E9%96%80-%E7%B5%90%E5%9F%8E-%E6%B5%A9/dp/4797327030/ref=sr_1_1?ie=UTF8&qid=1454743284&sr=8-1&keywords=java%E8%A8%80%E8%AA%9E%E3%81%A7%E5%AD%A6%E3%81%B6%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E5%85%A5%E9%96%80">増補改訂版Java言語で学ぶデザインパターン入門</a>