Design Pattern | Strategy Pattern (전략 패턴)


Strategy 패턴이란?

  • Strategy라는 영어 단어는 전략이라는 의미이다. 프로그래밍 경우에는 알고리즘 이라고 생각하면 이해하기 쉬울 것이다.
  • 프로그램은 문제를 해결하기 위해 작성된다. 문제를 해결하기 위해 특정 알고리즘이 구현되었다. Strategy 패턴은 알고리즘을 구현한 부분을 몰래 교환할 수 있는 방식이다.
  • 시스템이 유연하게 변경되고 확장될 수 있도록 한다.
  • 사용자(Client)는 자신에게 맞는 전략(Strategy)을 취사선택하여 로직을 수행할 수 있게하는 방법이다.
  • GoF의 디자인 패턴에서는 행위에 대한 디자인 패턴으로 분류된다.

예제 프로그램

가위바위보를 하는 프로그램이다. 무작위로 손을 내는 전략과 바위만의 손을 내는 전략이 있다.

Class Diagram
Strategy Pattern Class Diagram

1. Hand 클래스

가위바위보의 손을 나타내는 클래스이다.

Hand.java

package com.devkuma.designpattern.behavioral.strategy;

public class Hand {

    public static final int HAND_VALUE_ROCK = 0; // 바위
    public static final int HAND_VALUE_SCISSORS = 1; // 가위
    public static final int HAND_VALUE_PAPER = 2; // 보

    public static final Hand[] hand = {
            new Hand(HAND_VALUE_ROCK),
            new Hand(HAND_VALUE_SCISSORS),
            new Hand(HAND_VALUE_PAPER),
    };

    private int handValue;

    private Hand(int handValue) {
        this.handValue = handValue;
    }

    public static Hand getHand(int handValue) {
        return hand[handValue];
    }

    public boolean isStrongerThan(Hand hand) {
        // this가 hand보다 강했을 때 true
        return fight(hand) == 1;
    }

    private int fight(Hand hand) {
        if (this == hand) {
            // 무승부
            return 0;
        } else if ((this.handValue + 1) % 3 == hand.handValue) {
            // this의 승리
            return 1;
        } else {
            // hand의 승리
            return -1;
        }
    }
}

2. Player 클래스

가위바위보를 하는 플레이어를 나타내는 클래스이다.

Player.java

package com.devkuma.designpattern.behavioral.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) {
        this.name = name;
        this.strategy = strategy;
    }

    public String getName() {
        return name;
    }

    public Hand nextHand() {
        return strategy.nextHand();
    }

    public void win() {
        winCount++;
        gameCount++;
    }

    public void lose() {
        loseCount++;
        gameCount++;
    }

    public void even() {
        gameCount++;
    }

    public String toString() {
        return "[" + name + "] " + gameCount + " games, " + winCount + " win, " + loseCount + " lose";
    }
}

3. Strategy 인터페이스

가위바위보의 “전략"을 나타내는 인터페이스이다.

Strategy.java

package com.devkuma.designpattern.behavioral.strategy;

public interface Strategy {
    Hand nextHand();
}

4. RandomStrategy 클래스

무작위로 내는 전략을 나타내는 클래스이다.

RandomStrategy.java

package com.devkuma.designpattern.behavioral.strategy;

import java.util.Random;

public class RandomStrategy implements Strategy {

    public Hand nextHand() {
        Random random = new Random();
        return Hand.getHand(random.nextInt(3));
    }
}

5. RockStrategy 클래스

바위만 내는 전략을 나타내는 클래스이다.

GuuStrategy.java

package com.devkuma.designpattern.behavioral.strategy;

public class RockStrategy implements Strategy {

    public Hand nextHand() {
        return Hand.getHand(Hand.HAND_VALUE_ROCK);
    }
}

6. Main 클래스

메인 처리를 실시하는 클래스이다.

Main.java

package com.devkuma.designpattern.behavioral.strategy;

public class Main {

    public static void main(String[] args) {

        Player player1 = new Player("kimkc", new RandomStrategy());
        Player player2 = new Player("yunho", new RockStrategy());
        for (int i = 0; i < 5; i++) {
            Hand nextHand1 = player1.nextHand();
            Hand nextHand2 = player2.nextHand();
            if (nextHand1.isStrongerThan(nextHand2)) {
                System.out.println("Winner: " + player1.getName());
                player1.win();
                player2.lose();
            } else if (nextHand2.isStrongerThan(nextHand1)) {
                System.out.println("Winner: " + player2.getName());
                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);
    }
}

7. 실행 결과

Winner: kimkc
Winner: yunho
Even...
Even...
Winner: yunho
----- Total result -----
[kimkc] 5 gemes, 1 win, 2 lose
[yunho] 5 gemes, 2 win, 1 lose

Strategy 패턴의 장점

Strategy 패턴에서는 알고리즘의 부분을 다른 부분과 의식적으로 분리한다. 그리고 알고리즘과의 인터페이스의 부분만을 규정하여 위양에 의해 알고리즘을 이용한다.
이것은 프로그램을 복잡하게 만드는 것처럼 보이지만 그렇지 않는다. 예를 들어, 알고리즘을 개선하고 더 빨리 만들고 싶다면 Strategy 패턴을 사용하고 있다면 Strategy 역할의 인터페이스를 변경하지 않도록 알고리즘을 추가하고 수정해야 한다. 위양이라는 완만한 연결을 사용하고 있기 때문에 알고리즘을 준비로 전환할 수 있다.
그리고, 게임 프로그램등에서는 유저의 선택에 맞추어 난이도를 바꾸거나 하는데도 사용할 수 있다.