Both sides previous revisionPrevious revisionNext revision | Previous revision |
design_pattern:strategy_pattern [2017/10/29 09:40] – ledyx | design_pattern:strategy_pattern [2021/02/07 03:30] (current) – [Strategy Pattern] ledyx |
---|
**알고리즘(전략)을 전부 교체**해서 수정하기 쉽도록 만들기 | **알고리즘(전략)을 전부 교체**해서 수정하기 쉽도록 만들기 |
| |
{{tag>Architecture Modeling DesignPattern Behavioral}} | * 언제 쓰면 좋을까? |
| * 원래 알고리즘과 개선된 알고리즘의 속도 비교 |
| * 장기 게임에서 사용자의 난이도 선택에 따른 루틴 교체 |
| * 장점? |
| * 실행중에도 교체 가능! (예제 Client의 Player 초기화 부분 확인) |
| |
| {{tag>Architecture Modeling Design_Pattern Behavioral}} |
| |
| 시나리오 : 가위바위보 게임 |
| |
| = Strategy = |
| <sxh java ; collapse:true> |
| 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]; |
| } |
| } |
| </sxh> |
| |
| <sxh java ; title:Strategy> |
| public interface Strategy { |
| Hand nextHand(); |
| void study(boolean win); |
| } |
| </sxh> |
| |
| <sxh java ; title:ConcreteStrategy> |
| /* 이긴손 다시 내기 */ |
| 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; |
| } |
| } |
| </sxh> |
| |
| <sxh java ; title:ConcreteStrategy> |
| /* 과거 승패 이력으로 확률적으로 바꾸기 */ |
| 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]++; |
| } |
| } |
| } |
| </sxh> |
| |
| |
| = Context = |
| Strategy를 이용하는 역할. |
| |
| <sxh java ; title:Context ; highlight:[3]> |
| 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 + "]"; |
| } |
| } |
| </sxh> |
| |
| |
| = Client = |
| <sxh java> |
| 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] |
| */ |
| </sxh> |