Strategyパターンを触ってみる

# 今日の目標
JavaのStrategyパターンとはどういったものなのかを知る

# 使うもの
* [Java言語で学ぶデザインパターン入門](http://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=1449322953&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)

# ではスタート

## はじめに
まずはStrategyパターンとはどのようなものなのかをWikipediaさんで調べてみる。

>Strategyパターンは、コンピュータープログラミングの領域において、アルゴリズムを実行時に選択することができるデザインパターンである。（[wikipedia-Strategyパターン](https://ja.wikipedia.org/wiki/Strategy_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3)）

アルゴリズムのパターンをある程度用意しておいて、動的に切り替えられるようにするパターンのことかな。何となくわかるような。

## 本のサンプルを参考に自分で作ってみる
今回張り切ってサンプルを参考に自分で作ってみました。が、サンプルのほうがわかりやすいと思うので、わからなかったら本のほうを参考にしてください…。（本のサンプルはじゃんけんゲームです）

今回作るのは、テレビゲームのカジノとかでよく見かけるアップダウンゲームです。アップダウンゲームとは、トランプを一枚表にし、次にめくるカードがそのカードより「上か、下か」を当てる簡単なゲームです。これをStrategyパターンを使って作りました。
※本来ならトランプはA～Kまでですが、今回面倒だったので1～13という数字になってます。また、AはKより強いのですが、数字にした関係でAに当たる1は一番弱くなってます。ご了承ください

Strategyインタフェースは、shoutメソッドだけ定義してます。内部の実装は各実装クラスにお任せして、とりあえず「上だ！」「下だ！」ってことを叫んでね、ということ。

```Strategy.java
package strategy;

import util.ShoutWord;

public interface Strategy {

	// 受け取ったnumberによってUPとDOWNどちらを宣言するか
	public abstract ShoutWord shout(int number);

}
```

1つ目のStrategy実装はこんな感じ。単純。

* 1～6が出た場合⇒次のトランプはそれよりも上であると判断する
* 7～13が出た場合⇒次のトランプはそれよりも下であると判断する

``` HalfStrategy.java
package strategy;

import util.ShoutWord;

public class HalfStrategy implements Strategy {

	@Override
	public ShoutWord shout(int number) {
		ShoutWord shoutWord;

		// もし数字が7以上だったら次のカードはDOWNと宣言する
		if(number>=7) {
			shoutWord = ShoutWord.DOWN;
		} else {
			shoutWord = ShoutWord.UP;
		}
		return shoutWord;
	}
}
```

2つ目のStrategy実装はこれ。

* 1～4が出た場合⇒次のトランプはそれよりも上であると判断する
* 10～13が出た場合⇒次のトランプはそれよりも下であると判断する
* 5～9が出た場合⇒次の数字をランダムで予測して、予測した数字のほうが大きかったら上であると判断して、予測した数字のほうが小さかったら下であると判断する

``` OneThirdStrategy.java
package strategy;

import java.util.Random;

import util.ShoutWord;

public class OneThirdStrategy implements Strategy {

	@Override
	public ShoutWord shout(int number) {
		ShoutWord shoutWord;
		if(number>=10) {
			// 1-4: 上
			shoutWord = ShoutWord.DOWN;
		} else if(number<=4){
			// 10-13: 下
			shoutWord = ShoutWord.UP;
		} else {
			// 5-9: ランダム数字を生成し、次の数字を予想する
			Random random = new Random();
			int next = random.nextInt(13) + 1;
			if(number<=next) {
				shoutWord = ShoutWord.UP;
			} else {
				shoutWord = ShoutWord.DOWN;
			}
		}
		return shoutWord;
	}
}
```

PlayerはカードがめくられたらStrategyにお伺いを立てて、上か下かを叫びます。shout！

```Player.java
package strategy;

import util.ResultWord;
import util.ShoutWord;

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 ShoutWord shout(int number) {
		return strategy.shout(number);
	}

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

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

	public ResultWord result() {
		if(wincount>losecount) {
			return ResultWord.OK;
		}
		return ResultWord.NG;
	}

	public String toString() {
		return "[" + name + ":" + gamecount + " games, " + wincount + " win, " + losecount + " lose.";
	}
}
```

各enum。

```ShoutWord.java
package util;

public enum ShoutWord {
	UP,
	DOWN
}
```

```ResultWord.java
package util;

public enum ResultWord {
	OK,
	NG
}
```

実行するMainクラス。1000回勝負したうち、勝った回数と負けた回数を出力してます。
途中で、もし結果が「負けが多い」ということであれば（Playerのresultメソッド呼び出し）、別の戦略に乗り換えるようにしてます。OneThirdStrategyからHalfStrategyへ。（ロジックはコピペしてます。ホントはいけないんですが、今回はご容赦を）

```Main.java
import java.util.Random;

import strategy.HalfStrategy;
import strategy.OneThirdStrategy;
import strategy.Player;
import util.ResultWord;
import util.ShoutWord;

public class Main {

	public static void main(String[] args) {
		Random rnd = new Random();

		Player player = new Player("torinist", new OneThirdStrategy());
		int trumpNumberOld = rnd.nextInt(13) + 1;

		for(int i=0;i<1000;i++) {
			int trumpNumberNew = rnd.nextInt(13) + 1;
			ShoutWord shoutWord = player.shout(trumpNumberOld);

			// 判定
			if(trumpNumberOld<trumpNumberNew) {
				// もし新しいカードのほうが数字が大きい場合
				if(ShoutWord.UP==shoutWord) {
					player.win();
				} else {
					player.lose();
				}
			} else if(trumpNumberOld>trumpNumberNew) {
				// もし新しいカードのほうが数字が小さい場合
				if(ShoutWord.DOWN==shoutWord) {
					player.win();
				} else {
					player.lose();
				}
			} else {
				// 同じ数字の場合は自動的にPlayerがwinになる
				player.win();
			}

			trumpNumberOld = trumpNumberNew;
		}

		if(ResultWord.NG == player.result()) {
			System.out.println("HalfStrategyに移行します。");
			player = new Player("torinist", new HalfStrategy());
			trumpNumberOld = rnd.nextInt(13) + 1;

			for(int i=0;i<1000;i++) {
				int trumpNumberNew = rnd.nextInt(13) + 1;
				ShoutWord shoutWord = player.shout(trumpNumberOld);

				// 判定
				if(trumpNumberOld<trumpNumberNew) {
					// もし新しいカードのほうが数字が大きい場合
					if(ShoutWord.UP==shoutWord) {
						player.win();
					} else {
						player.lose();
					}
				} else if(trumpNumberOld>trumpNumberNew) {
					// もし新しいカードのほうが数字が小さい場合
					if(ShoutWord.DOWN==shoutWord) {
						player.win();
					} else {
						player.lose();
					}
				} else {
					// 同じ数字の場合は自動的にPlayerがwinになる
					player.win();
				}

				trumpNumberOld = trumpNumberNew;
			}
		}

		System.out.println("Total result: " + player.toString());
	}

}
```

結果はこんな感じ。ぶっちゃけOneThirdStrategyが負けることがないので、HalfStrategyには移行しません。残念。
```Total result: [torinist:1000 games, 782 win, 218 lose.```


注目すべきは、Mainの中でPlayerが持つべきStrategyを変更しているということですかね。たぶんこの動的な切り替えがStrategyパターンなのだと理解してます。違ったら訂正して頂けるとありがたいです。
もし、戦略を増やしたい！ということであれば、Strategyインタフェースを実装したクラスを作って、MainでPlayerに渡すStrategy実装を変えれば良いのですね。

今日はこんな感じで！