- Strategy 패턴 : 알고리즘을 모두 바꾼다
Strategy 패턴은 스위치 전환하듯 알고리즘을 바꿔, 같은 문제를 다른 방법으로 해결하기 쉽게 만드는 패턴이다.
- 어떤 경우에 사용할까?
보통 프로그래밍을 하다 보면 알고리즘을 메서드 안에 녹아드는 형태로 구현하기 쉬운데, Strategy 패턴을 사용하면 알고리즘 부분을 다른 부분과 의도적으로 분리한다. 이로 인해 알고리즘을 용이하게 전환할 수 있다.
- 예제 코드
이름 | 설명 |
Hand | 가위바위보의 '손'을 나타내는 클래스 |
Strategy | 가위바위보의 '전략'을 나타내는 인터페이스 |
WinningStrategy | 이기면 다음에도 같은 손을 내는 전략을 나타내는 클래스 |
ProbStrategy | 직전 손에서 다음 손을 확률적으로 계산하는 전략을 나타내는 클래스 |
Player | 가위바위보를 하는 플레이어를 나타내는 클래스 |
Main | 동작 테스트용 클래스 |
- Hand형
public enum Hand {
// 가위 바위 보를 나타내는 세 개의 enum 상수
ROCK("바위", 0),
SCISSORS("가위", 1),
PAPER("보", 2);
// enum이 가진 필드
private String name; // 가위 바위 보 손의 이름
private int handvalue; // 가위 바위 보 손의 값
private static Hand[] hands = {
ROCK, SCISSORS, PAPER
};
// 생성자
private Hand(String name, int handvalue) {
this.name = name;
this.handvalue = handvalue;
}
// 손의 값으로 enum 상수를 가져온다
public static Hand getHand(int handvalue) {
return hands[handvalue];
}
// this가 h보다 강할 때 true
public boolean isStrongerThan(Hand h) {
return fight(h) == 1;
}
// this가 h보다 약할 때 true
public boolean isWeakerThan(Hand h) {
return fight(h) == -1;
}
// 무승부는 0, this가 이기면 1, 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;
}
}
- Strategy 인터페이스
public interface Strategy {
public abstract Hand nextHand();
public abstract void study(boolean win);
}
- WinningStrategy 클래스
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;
}
}
- ProbStrategy 클래스
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);
}
@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 handvalue) {
int sum = 0;
for(int i = 0 ; i < 3 ; i++) {
sum += history[handvalue][i];
}
return sum;
}
@Override
public void study(boolean win) {
if(win) {
history[prevHandValue][currentHandValue]++;
} else {
history[prevHandValue][(currentHandValue + 1) % 3]++;
history[prevHandValue][(currentHandValue + 2) % 3]++;
}
}
}
- Player 클래스
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++;
}
@Override
public String toString() {
return "["
+ name + ":"
+ gamecount + " games, "
+ wincount + " win, "
+ losecount + " lose"
+ "]";
}
}
- Main 클래스
public class Main {
public static void main(String[] args) {
if(args.length != 2) {
System.out.println("Usage: java Main randomseed1 randomseed2");
System.out.println("Example: java Main 314 15");
System.exit(0);
}
int seed1 = Integer.parseInt(args[0]);
int seed2 = Integer.parseInt(args[1]);
Player player1 = new Player("KIM", new WinningStrategy(seed1));
Player player2 = new Player("LEE", new ProbStrategy(seed2));
for(int i = 0 ; i < 10000 ; i++) {
Hand nextHand1 = player1.nextHand();
Hand nextHand2 = player2.nextHand();
if(nextHand1.isStrongerThan(nextHand2)) {
System.out.println("Winner:" + player1);
player1.win();
player2.lose();
}
else if(nextHand2.isStrongerThan(nextHand1)) {
System.out.println("Winner:" + player2);
player1.lose();
player2.win();
}
else {
System.out.println("Even...");
player1.even();
player2.even();
}
}
System.out.println("Total result:");
System.out.println(player1);
System.out.println(player2);
}
}
참고문헌 : 유키 히로시 저/김성훈 역(2022), JAVA 언어로 배우는 디자인 패턴 입문, 영진닷컴
참고자료 : https://refactoring.guru/ko/design-patterns/strategy