본문 바로가기
개발/디자인 패턴

[Java 언어로 배우는 디자인 패턴 입문] 분리해서 생각하기 10. Strategy

by hongdor 2021. 1. 10.
728x90

출처 : 책 - java 언어로 배우는 디자인 패턴 입문

 

10. Strategy - 알고리즘을 모두 바꾸기

 

 

1. 목적

 

 - 실행 알고리즘(전략) 클래스를 분리 해 인스턴스를 교체할 수 있도록 한다.

 > Build 패턴과 비슷하지만 다른점이라고 생각되는 것은 Build 패턴은 인스턴드 생성에 관한 것이라 

    고정적인 느낌이고

    Strategy 패턴은 전략 클래스에 관련된 것이기에 실행 도중에 교체 하기도 하는 동적인 느낌이다.

    (참고 : 전략 패턴 - 위키백과, 우리 모두의 백과사전 (wikipedia.org))

 

public static void Main(String[] args)
    {
        Customer firstCustomer = new Customer(new NormalStrategy());

        // Normal billing
        firstCustomer.Add(1.0, 1);

        // Start Happy Hour
        firstCustomer.Strategy = new HappyHourStrategy();
        firstCustomer.Add(1.0, 2);
}
        

> 위키백과의 예시로 여기서 Strategy는 세일기간 시작에 따른 물건 가격 변경이다.

 

Strategy Pattern vs Builder Pattern | by Pratik Raj | Medium

> Builder is a creational pattern and is used to create complex objects. Builder knows how to create( and sometimes assemble) objects and is used to encapsulate the complexity from the clients. Strategy on the other hand is a behavioral pattern ie., it is to do different “things” with different objects at runtime.

> 빌더는 작성 패턴이며 복잡한 오브젝트를 생성하는 데 사용됩니다. Builder는 객체를 생성하는 방법을 알고 있으며(때로는 조립) 클라이언트의 복잡성을 캡슐화하는 데 사용됩니다. 반면에 전략은 행동 패턴입니다. 즉, 런타임에 서로 다른 개체들을 가지고 다른 "일"을 수행하는 것입니다.

 

 

2. 예제

 

(1) Hand 

public class Hand {
    public static final int HANDVALUE_GUU = 0;  // 주먹을 표시하는 값
    public static final int HANDVALUE_CHO = 1;  // 가위를 표시하는 값
    public static final int HANDVALUE_PAA = 2;  // 보를 표시하는 값
    public static final Hand[] hand = {         // 가위바위보의 손을 표시하는 3개의 인스턴스
        new Hand(HANDVALUE_GUU),
        new Hand(HANDVALUE_CHO),
        new Hand(HANDVALUE_PAA),
    };
    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 isStrongerThan(Hand h) {     // this가 h를 이길 경우 true
        return fight(h) == 1;
    }
    public boolean isWeakerThan(Hand h) {       // this가 h에게 질 경우 true
        return fight(h) == -1;
    }
    private int fight(Hand h) {                 // 무승부는 0, this의 승이면 1, h의 승이면 -1
        if (this == h) {
            return 0;
        } else if ((this.handvalue + 1) % 3 == h.handvalue) {
            return 1;
        } else {
            return -1;
        }
    }
    public String toString() {                  // 문자열 표현으로 변환
        return name[handvalue];
    }
}

 

 

(2) 전략 인터페이스

public interface Strategy {
    public abstract Hand nextHand();
    public abstract void study(boolean win);
}

 

 

(3) 전략 구현1

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);
    }
    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) {
        int sum = 0;
        for (int i = 0; i < 3; i++) {
            sum += history[hv][i];
        }
        return sum;
    }
    public void study(boolean win) {
        if (win) {
            history[prevHandValue][currentHandValue]++;
        } else {
            history[prevHandValue][(currentHandValue + 1) % 3]++;
            history[prevHandValue][(currentHandValue + 2) % 3]++;
        }
    }
}

 

 

(4) 전략 구현2

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);
    }
    public Hand nextHand() {
        if (!won) {
            prevHand = Hand.getHand(random.nextInt(3));
        }
        return prevHand;
    }
    public void study(boolean win) {
        won = win;
    }
}

 

 

(5) 플레이어

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++;
    }
    public String toString() {
        return "[" + name + ":" + gamecount + " games, " + wincount + " win, " + losecount + " lose" + "]";
    }
}

 

 

(6) 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("두리", new WinningStrategy(seed1));
        Player player2 = new Player("하나", 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.toString());
        System.out.println(player2.toString());
    }
}
728x90

댓글