新人SEの学習記録

14年度入社SEの学習記録用に始めたブログです。もう新人じゃないかも…

学習記録:デザインパターン

[学習記録] デザインパターン

内容 第10章:Strategyパターン

概要
  • どんなプログラムにも、問題を解くための特定のアルゴリズムが実装されている
    • Strategyパターンでは、そのアルゴリズム部分をごっそり入れ替えることができる
    • アルゴリズム(戦略・作戦)を切り替え、同じ問題を別の解法で解くのを容易にするパターン
サンプルプログラム
  • Eカードもどき。
    • 細かいルール等は抜きで、皇帝は市民に、市民は奴隷に、奴隷は皇帝に勝つというだけのゲーム。
    • 作り込みたかったけど本質ではないので…
Card カードの役を表す
Strategy 戦略を表すインタフェース
RandomStrategy 勝ったらランダム、負けたらそのときの相手に勝つ手を出す戦略
MimicStrategy 前に相手が出した手を真似る戦略
Player プレイヤーを表す
Main 動作テスト用
  • Card
    • 役の値をcardValueで表現。
    • Cardクラスのインスタンスは3つしか作られず、getCardで取得する。一種のSingleton。
    • isStrongerThanメソッドで2つの手を比較する。
package chap10;

public class Card {
	public static final int CARD_EMP = 0;
	public static final int CARD_CTZ = 1;
	public static final int CARD_SLV = 2;
	public static final Card[] card = {
		new Card(CARD_EMP), 
		new Card(CARD_CTZ),
		new Card(CARD_SLV),
	};
	private static final String[] name = {"皇帝", "市民", "奴隷",};
	private int cardValue;

	private Card(int cardValue) {
		this.cardValue = cardValue;
	}
	public static Card getCard(int cardValue) {
		return card[cardValue % 3];
	}
	public boolean isStrongerThan(Card c) {
		return ((this.cardValue + 1) % 3 == c.cardValue); 
	}
	public int getValue() {
		return cardValue;
	}
	@Override
	public String toString() {
		return name[cardValue];
	}
}
  • Strategy
    • 戦略のための抽象メソッドを集めたもの。
    • nextCardは次に出すカードを得るためのメソッド
    • studyはさっき出した手で勝ったかどうかを学習するメソッド
public interface Strategy {
	public abstract Card nextCard();
	public abstract void study(boolean win);
}
  • RandomStrategy
    • Strategyインタフェースを実装。
    • 前に出した手とそれで勝ったかどうかを保持。
    • 勝ったら次の手はランダム、負けたらそのときの相手の手に勝てるカードを出す。
import java.util.Random;

public class RandomStrategy implements Strategy {
	private Random random;
	private boolean won = false;
	private Card prevCard;
	public RandomStrategy(long seed) {
		random = new Random(seed);
	}
	@Override
	public Card nextCard() {
		if (won) {
			prevCard = Card.getCard(random.nextInt(3));
		}
		else {
			prevCard = Card.getCard((prevCard.getValue() + 1) % 3);
		}
		return prevCard;
	}

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

}
  • MimicStrategy
    • 前に相手が出した手を真似る戦略。
    • ただし、引き分けを認識できないので、完全に真似するわけではない。
public class MimicStrategy implements Strategy {
	private Card prevCard = Card.getCard(0);
	private boolean won = false;
	@Override
	public Card nextCard() {
		if (won) {
			prevCard = Card.getCard((prevCard.getValue() + 1) % 3);
		}
		else {
			prevCard = Card.getCard((prevCard.getValue() + 2) % 3);
		}
		return prevCard;
	}

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

}
  • Player
    • プレイヤーを表す。
    • 書籍のサンプルではCOM同士戦うが、ユーザが入力できるように変えたのでこの場合正確にはCOM側を表す。
    • 戦略を与えられ、次に出す手を戦略から導出する。nextHandの処理はStrategyに委譲されている。
    • 勝ち負けを記録。
public class Player {
	private Strategy strategy;
	private int win, lose, game;
	public Player(Strategy strategy) {
		this.strategy = strategy;
	}
	public Card nextCard() {
		return strategy.nextCard();
	}
	public void win() {
		strategy.study(true);
		win++;
		game++;
	}
	public void lose() {
		strategy.study(false);
		lose++;
		game++;
	}
	public void even() {
		game++;
	}
	@Override
	public String toString() {
		return String.format("[Player: %d games, %d win, %d lose]", game, lose, win);
	}

}
  • Main
    • 入力を受けてCOMと戦う。
    • COMの戦略はランダム(そのときの時間)で決まる。
import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		Player cpu;
		if (System.currentTimeMillis() % 2 == 0) {
			cpu = new Player(new RandomStrategy(System.currentTimeMillis()));		
		} else {
			cpu = new Player(new MimicStrategy());
		}
		System.out.println("ゲームを開始します");
		
		for (int i = 1; i <= 5; i++) {
			System.out.println("=== " + i + "回戦 ===");
			System.out.println("0: 皇帝 1: 市民 2: 奴隷");
			System.out.print("出すカードを入力してください: ");
			Card playerCard = Card.getCard(scan.nextInt());
			Card cpuCard = cpu.nextCard();
			
			System.out.println("あなた: " + playerCard.toString());
			System.out.println("COM  : " + cpuCard.toString());
			
			if (playerCard.isStrongerThan(cpuCard)) {
				System.out.println("勝利!");
				cpu.lose();
			}
			else if (cpuCard.isStrongerThan(playerCard)) {
				System.out.println("敗北...");
				cpu.win();
			}
			else {
				System.out.println("引き分け");
				cpu.even();
			}
			System.out.println(cpu.toString());
		}
		scan.close();
	}
}
ゲームを開始します
=== 1回戦 ===
0: 皇帝 1: 市民 2: 奴隷
出すカードを入力してください: 1
あなた: 市民
COM  : 奴隷
勝利!
[Player: 1 games, 1 win, 0 lose]
...
まとめと補足
  • Strategyパターンの登場人物
    • Strategy:戦略を利用するためのインタフェースを表す。
    • ConcreteStrategy:Strategy役のインタフェースを実装する役。具体的な戦略をプログラミングする。
    • Context:Strategy役を利用する役。サンプルのPlayer。