전략 패턴(Strategy Pattern)

  • 전략 패턴은 객체 내부에서 동적으로 알고리즘을 교체하기 위한 패턴입니다.
  • 여기서 말하는 알고리즘이란 어떤 문제를 해결하기 위한 방법을 의미합니다.
  • 따라서 어떤 문제를 해결하기 위한 함수 또는 로직을 객체로 분리하여 런타임 상황에서 교체하는 패턴입니다.
  • 템플릿 메서드(Template Method)와 유사한 부분이 존재합니다.
  • 템플릿 메서드는 하나의 알고리즘을 다양한 방식으로 재정의(오버라이딩)하여 행동을 분리하는 패턴입니다. 하지만 공통 템플릿도 수정해야할 경우 상위 클래스도 수정해야 하는 단점이 있습니다.
  • 전략 패턴은 템플릿 메서드와는 다르게 행위의 전체를 변경할 때 사용하는 패턴입니다.

전략 패턴 구조

  • 알고리즘을 교체하기 위해서는 공통된 처리가 필요합니다.
  • 따라서 알고리즘 인터페이스 클래스와 실제 구현체가 필요합니다.
  • 또한 알고리즘 인터페이스를 이용하여 전략을 교체할 전략 추상 클래스와 실제 구현체가 필요합니다.
  • 따라서 아래와 같이 정리할 수 있습니다.
    • Algorithm
    • ConcreteAlgorithm
    • Strategy
    • ConcreteStrategy

전략 패턴: 알고리즘 인터페이스 클래스

  • 알고리즘 교체를 위한 공통된 인터페이스 클래스를 선언합니다.
  • 게임에서 캐릭터가 무기를 교체하여 사용하는 예제를 사용하겠습니다.
1
2
3
4
5
6
// Algorithm
class Weapon
{
public:
    virtual void attack() = 0;
};

전략 패턴: 알고리즘 클래스 구현

  • 교체할 알고리즘들을 실제로 구현합니다.
  • 공통된 인터페이스 클래스로 서로 다른 알고리즘을 갖고 있습니다.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// ConcreteAlgorithm
class Knife :
    public Weapon
{
public:
    void attack() override
    {
        std::cout << "[Knife] Attack()" << std::endl;
    }
};

// ConcreteAlgorithm
class Gun :
    public Weapon
{
public:
    void attack() override
    {
        std::cout << "[Gun] Attack()" << std::endl;
    }
};

전략 패턴: 전략 추상 클래스 구현

  • 알고리즘을 런타임 상황에서 교체할 추상 클래스가 필요합니다.
  • 내부적으로 알고리즘 객체를 저장할 프로퍼티가 존재하고, 이를 지정하는 setWeapon() 메서드가 필요합니다.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Strategy
{
public:
    void setWeapon(std::shared_ptr<Weapon> weapon)
    {
        m_weapon = weapon;
    }

    virtual void attack() = 0;

protected:
    std::shared_ptr<Weapon> m_weapon;
};

전략 패턴: 전략 클래스 구현

  • 전략 클래스의 실제적인 하위 클래스를 구현합니다.
  • 알고리즘 인터페이스 클래스의 메서드를 호출하기 때문에 특정 알고리즘에 종속되지 않습니다.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// ConcreteStrategy
class Character :
    public Strategy
{
public:
    void attack()
    {
        if (m_weapon == nullptr)
        {
            std::cout << "[Character] attack() hand" << std::endl;
        }
        else
        {
            m_weapon->attack();
        }
    }
};

전략 패턴: 실행 코드

  • 전략 패턴은 구조가 간단하고 이해하기 쉽습니다.
  • 동일한 USB 인터페이스를 통해 키보드, 마우스, 프린터 등 다양한 장치를 처리하는 알고리즘을 생각하면 그 구조를 쉽게 이해할 수 있습니다.
  • 템플릿 메서드와 같이 많이 사용하는 패턴 중 하나입니다.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
int main(const int argc, const char* argv[])
{
    std::shared_ptr<Character> Character = std::make_shared<Character>();
    Character->attack();

    std::shared_ptr<Knife> knife = std::make_shared<Knife>();
    Character->setWeapon(knife);
    Character->attack();

    std::shared_ptr<Gun> gun = std::make_shared<Gun>();
    Character->setWeapon(gun);
    Character->attack();

    return 0;
}
  • 실행 결과
1
2
3
[Character] attack() hand
[Knife] Attack()
[Gun] Attack()