JAVA

[디자인패턴] 전략 패턴(Strategy Pattern)

gom1n 2022. 4. 7. 02:35

전략 패턴(Strategy Pattern)

- 직접 행위를 수정하지 않고 전략을 바꿔주기만 함으로써 행위를 유연하게 확장하는 방법

- 알고리즘을 구현한 부분이 모두 교환 가능하도록 함

- 알고리즘(전략, 작전, 책략)을 교체해서 동일한 문제를 다른 방법으로 해결하는 패턴

 

예제 프로그램

컴퓨터에서 '가위 바위 보'를 실행하는 프로그램. 두가지 전략을 쓴다.

전략1) Winning strategy : 이기면 다음에도 같은 손을 내린다.

전략2) Prob Strategy : 직전에 냈던 손에서 다음 낼 손을 확률적으로 계산한다.

 

다이어그램)

 

Source Code)

Hand.class

더보기
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 = {         
        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) {    
        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;
        }
    }
    public String toString() {                 
        return name[handvalue];
    }
}

Player.class

더보기
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" + "]";
    }
}

Strategy 인터페이스

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

 

WinningStrategy.class

더보기
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;
    }
}

ProbStrategy.class

더보기
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]++;
        }
    }
}

 

Main.class

더보기
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());
    }
}

 

출력결과)

Usage: java Main randomseed1 randomseed2
Example: java Main 314 15

 

 


연습문제 10-4)

다음 프로그램은 소트(정렬)를 실행하기 위한 클래스와 인터페이스입니다. 이 프로그램의 실행결과는 아래와 같습니다. 여기에서는 알고리즘으로 선택 정렬(selection sort)을 이용하고 있습니다. 다른 알고리즘을 표현하는 클래스를 Sorter의 인터페이스에 맞추어서 작성하십시오.

 

실행결과)

Dumpty, Bowman, Carroll, Elfland, Alice.	//정렬 전
Alice, Bowman, Carroll, Dumpty, Elfland. 	//정렬 후

다이어그램)

 

Source code)

Sorter 인터페이스

import java.lang.Comparable;

public interface Sorter {
    public abstract void sort(Comparable[] data);
}

SelectionSorter 클래스

public class SelectionSorter implements Sorter {
    public void sort(Comparable[] data) {
        for (int i = 0; i < data.length - 1; i++) {
            int min = i;
            for (int j = i + 1; j < data.length; j++) {
                if (data[min].compareTo(data[j]) > 0) {
                    min = j;
                }
            }
            Comparable passingplace = data[min];
            data[min] = data[i];
            data[i] = passingplace;
        }
    }
}

SortAndPrint 클래스

public class SortAndPrint {
    Comparable[] data;
    Sorter sorter;
    public SortAndPrint(Comparable[] data, Sorter sorter) {
        this.data = data;
        this.sorter = sorter;
    }
    public void execute() {
        print();
        sorter.sort(data);
        print();
    }
    public void print() {
        for (int i = 0; i < data.length; i++) {
            System.out.print(data[i] + ", ");
        }
        System.out.println("");
    }
}

Main클래스

public class Main {
    public static void main(String[] args) {
        String[] data = {
            "Dumpty", "Bowman", "Carroll", "Elfland", "Alice",
        };
        SortAndPrint sap = new SortAndPrint(data, new SelectionSorter());
        sap.execute();
    }
}

 

 

답)

QuickSorter는 quick sort algorithm을 사용한 클래스입니다.

QuickSorter 클래스

public class QuickSorter implements Sorter {
    Comparable[] data;
    public void sort(Comparable[] data) {
        this.data = data;
        qsort(0, data.length - 1);
    }
    private void qsort(int pre, int post) {
        int saved_pre = pre;
        int saved_post = post;
        Comparable mid = data[(pre + post) / 2];
        do {
            while (data[pre].compareTo(mid) < 0) {
                pre++;
            }
            while (mid.compareTo(data[post]) < 0) {
                post--;
            }
            if (pre <= post) {
                Comparable tmp = data[pre];
                data[pre] = data[post];
                data[post] = tmp;
                pre++;
                post--;
            }
        } while (pre <= post);
        if (saved_pre < post) {
            qsort(saved_pre, post);
        }
        if (pre < saved_post) {
            qsort(pre, saved_post);
        }
    }
}

Main 클래스

public class Main {
    public static void main(String[] args) {
        String[] data = {
            "Dumpty", "Bowman", "Carroll", "Elfland", "Alice",
        };
        SortAndPrint sap = new SortAndPrint(data, new QuickSorter());
        sap.execute();
    }
}