= Strategy Pattern =
**알고리즘(전략)을 전부 교체**해서 수정하기 쉽도록 만들기
* 언제 쓰면 좋을까?
* 원래 알고리즘과 개선된 알고리즘의 속도 비교
* 장기 게임에서 사용자의 난이도 선택에 따른 루틴 교체
* 장점?
* 실행중에도 교체 가능! (예제 Client의 Player 초기화 부분 확인)
{{tag>Architecture Modeling Design_Pattern Behavioral}}
시나리오 : 가위바위보 게임
= Strategy =
public class Hand {
public static final int HANDVALUE_ROCK = 0;
public static final int HANDVALUE_SCISSORS = 1;
public static final int HANDVALUE_PAPER = 2;
private static final Hand[] hand = {
new Hand(HANDVALUE_ROCK),
new Hand(HANDVALUE_SCISSORS),
new Hand(HANDVALUE_PAPER)
};
private static final String[] name = {
"주먹", "가위", "보"
};
private int handValue;
private Hand(int handValue) {
this.handValue = handValue;
}
public static Hand getHand(int handValue) {
return hand[handValue];
}
public boolean isStgrongerThan(Hand h) {
return fight(h) == 1;
}
public boolean isWeakerThan(Hand h) {
return fight(h) == -1;
}
private int fight(Hand h) {
if(this == h)
return 0;
else if((this.handValue + 1) % 3 == h.handValue)
return 1;
else
return -1;
}
@Override
public String toString() {
return name[handValue];
}
}
public interface Strategy {
Hand nextHand();
void study(boolean win);
}
/* 이긴손 다시 내기 */
public class WinningStrategy implements Strategy {
private Random random;
private boolean won = false;
private Hand prevHand;
public WinningStrategy() {
this.random = new Random();
}
@Override
public Hand nextHand() {
if(!won)
prevHand = Hand.getHand(random.nextInt(3));
return prevHand;
}
@Override
public void study(boolean win) {
won = win;
}
}
/* 과거 승패 이력으로 확률적으로 바꾸기 */
public class ProbStrategy implements Strategy {
private Random random;
private int prevHandValue;
private int currentHandValue;
private int[][] history = {
{1, 1, 1},
{1, 1, 1},
{1, 1, 1}
};
public ProbStrategy() {
this.random = new Random();
}
@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) {
return IntStream.range(0, 3).map(i -> history[hv][i]).sum();
}
@Override
public void study(boolean win) {
if(win)
history[prevHandValue][currentHandValue]++;
else {
history[prevHandValue][(currentHandValue + 1) % 3]++;
history[prevHandValue][(currentHandValue + 2) % 3]++;
}
}
}
= Context =
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) {
super();
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(true);
loseCount++;
gameCount++;
}
public void even() {
gameCount++;
}
@Override
public String toString() {
return "Player [name=" + name + ", winCount=" + winCount + ", loseCount=" + loseCount
+ ", gameCount=" + gameCount + "]";
}
}
= Client =
public class Main {
public static void main(String[] args) {
Player player1 = new Player("철수", new WinningStrategy());
Player player2 = new Player("영희", new ProbStrategy());
IntStream.range(0, 10000).forEach(i -> {
Hand nextHand1 = player1.nextHand();
Hand nextHand2 = player2.nextHand();
if(nextHand1.isStgrongerThan(nextHand2)) {
System.out.println("Winner : " + player1.toString());
player1.win();
player2.lose();
}
else if(nextHand2.isStgrongerThan(nextHand1)) {
System.out.println("Winner : " + player2.toString());
player1.lose();
player2.win();
}
else {
System.out.println("Even!");
player1.even();
player2.even();
}
});
System.out.println("\nTotal result:");
System.out.println(player1.toString());
System.out.println(player2.toString());
}
}
/*
...
Winner : Player [name=영희, winCount=7843, loseCount=2135, gameCount=9997]
Winner : Player [name=영희, winCount=7844, loseCount=2135, gameCount=9998]
Winner : Player [name=영희, winCount=7845, loseCount=2135, gameCount=9999]
Total result:
Player [name=철수, winCount=2135, loseCount=7846, gameCount=10000]
Player [name=영희, winCount=7846, loseCount=2135, gameCount=10000]
*/