Strategyパターンとは
どんなプログラムも問題を解くために書かれている。
そして問題を解くために特定のアルゴリズムが実装されている。
Strategyパターンでは、そのアルゴリズムを実装した部分が交換できるようになっている。
アルゴリズムを切り替え、同じ問題を別の方法で解くのを容易にするパターン。
Strategy(戦略)の役
戦略を利用するためのインターフェースを定める役。
package strategy;
public interface Strategy {
// 次回出す手を決定する
public abstract Hand nexHand();
// 前回出した手で勝ったかどうかを学習する
public abstract void study(boolean win);
}
ConcreteStrategy(具体的戦略)の役
Strategy役のインターフェースを実際に実装する役。
ここで具体的に戦略(作戦、方法、方策、アルゴリズム)を実際にプログラミングする。
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;
}
}
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役のインターフェース。
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" + "]";
}
}
呼び出し元
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);
}
}
実行結果
こちらを参考にさせていただきました。
増補改訂版Java言語で学ぶデザインパターン入門