전략 패턴

전략(Strategy) 패턴은 특정한 작업(알고리즘)을 독립적으로 정의하고 캡슐화 하여, 해당 작업을 동적으로 교체할 수 있도록 하는 패턴을 의미합니다.
즉, 특정 기능을 인터페이스로 분리하고, 다양한 구현체를 만들어서 필요할 때 변경할 수 있도록 합니다.

한 가지 행동(알고리즘)을 필요에 따라 외부(클라이언트)가 자유롭게 갈아 끼울 수 있도록 하는 패턴입니다.

전략 패턴은 동일한 작업을 다른 방법(전략)으로 수행하기 위해 객체 합성을 활용합니다.

세 가지 주요 구성 요소로 이루어집니다.

  1. 전략 인터페이스(Strategy): 특정 알고리즘의 인터페이스를 정의합니다.
  2. 구체적인 전략 클래스(ConcreteStrategy): 전략 인터페이스를 구현하여 알고리즘을 구체화하는 클래스입니다.
  3. 전략을 사용하는 클래스(Context): 전략 객체를 포함하며, 실행 시점에서 전략을 선택하여 실행합니다.

자주 바뀌는 로직(할인 정책, 무기 시스템, 정렬 알고리즘 등)에 대해서 전략 패턴을 적용하면 유리합니다.
복잡한 if-else 또는 switch문으로 분기 처리하던 로직을 행동 객체로 분리함으로써 코드 가독성과 유지보수성을 높일 수 있습니다.

  • 장점
    • 새로운 행동 클래스만 추가하면 되므로, 기존 코드 영향이 적는 유연성을 가집니다.
    • 인터페이스만 알면 되므로, 구체 구현 변경 시 수정 범위가 최소화되어 결합도가 감소됩니다.
    • 프로그램 실행 중에 행동을 교체할 수 있습니다.
    • 여러 클래스에서 동일한 전략을 재사용할 수 있습니다.
  • 단점
    • 여러 전략 인터페이스 및 클래스를 생성해야 하므로 클래스의 개수가 많아집니다.
    • 생성자 혹은 팩토리에서 어떤 행동을 쓸지 결정하는 과정이 필요합니다.

정렬 알고리즘 교체, 할인 정책 교체 등의 시나리오에서 유용합니다.

예시

// 행동 인터페이스
class IFlyBehavior
{
public:
    virtual void Fly() = 0;
};

class IQuackBehavior
{
public:
    virtual void Quack() = 0;
};

IFlyBehaviorIQuackBehavior는 오리의 행동(날기, 울기)을 추상화한 인터페이스입니다.

Fly함수와 Quack함수는 순수 가상 함수로 선언되어 있으며, 이를 상속받는 클래스에서 반드시 구현해야 합니다.
오리의 비행 및 울음소리 행동을 개별적으로 변경할 수 있도록 만듭니다.

// 인터페이스 기반 구체적인 행동 구현
class FlyWithWings : public IFlyBehavior
{
    virtual void Fly() override
    {
        cout << "날개로 납니다." << endl;
    }
};

class FlyNoWay : public IFlyBehavior
{
    virtual void Fly() override
    {
        cout << "날지 못합니다." << endl;
    }
};

class FlyRocketPowered : public IFlyBehavior
{
    virtual void Fly() override
    {
        cout << "로켓 추진으로 엄청나게 날아갑니다!!!" << endl;
    }
};

class QuackQuack : public IQuackBehavior
{
public:
    virtual void Quack() override
    {
        cout << "꽥꽥 소리를 냅니다." << endl;
    }
};

class MuteQuack : public IQuackBehavior
{
public:
    virtual void Quack() override
    {
        cout << "... (침묵)" << endl;
    }
};

class SqueakQuack : public IQuackBehavior
{
public:
    virtual void Quack() override
    {
        cout << "삑삑 소리를 냅니다." << endl;
    }
};
  • FlyWithWings: 날개를 사용하여 나는 행동을 구현.
  • FlyNoWay: 날지 못하는 행동을 구현.
  • FlyRocketPowered: 로켓을 이용하여 빠르게 나는 행동을 구현.

  • QuackQuack: 기본적으로 오리가 “꽥꽥” 소리를 냄.
  • MuteQuack: 소리를 내지 않는 행동.
  • SqueakQuack: 장난감 오리처럼 “삑삑” 소리를 내는 행동.
// 인터페이스를 포함하는 클래스 정의
class Duck
{
public:
    virtual ~Duck()
    {
        delete flyBehavior;
        delete quackBehavior;
    }

    virtual void Display() {}

    virtual void PerformFly()
    {
        flyBehavior->Fly();
    }

    void PerformQuack()
    {
        quackBehavior->Quack();
    }

    void SetFlyBehavior(IFlyBehavior* fb)
    {
        delete flyBehavior;
        flyBehavior = fb;
    }

    void SetQuackBehavior(IQuackBehavior* qb)
    {
        delete quackBehavior;
        quackBehavior = qb;
    }

protected:
    IFlyBehavior* flyBehavior;
    IQuackBehavior* quackBehavior;
};

Duck은 오리의 기본 클래스 이며, 행동을 인터페이스(IFlyBehavior, IQuackBehavior)로 정의했습니다.

PerformFly함수와 PerformQuack함수를 통해 현재 설정된 비행 및 울음 행동을 실행합니다.
SetFlyBehavior함수와 SetQuackBehavior함수를 통해 런타임 중에 행동을 동적으로 변경할 수 있습니다.
소멸자에서 flyBehaviorquackBehaviordelete하여 메모리 해제합니다.

// 구체적인 클래스 정의
class MallardDuck : public Duck
{
public:
    MallardDuck()
    {
        flyBehavior = new FlyWithWings();
        quackBehavior = new QuackQuack();
    }

    virtual void Display() override
    {
        cout << "Mallard 오리" << endl;
    }
};

class RubberDuck : public Duck
{
public:
    RubberDuck()
    {
        flyBehavior = new FlyNoWay();
        quackBehavior = new SqueakQuack();
    }

    virtual void Display() override
    {
        cout << "Rubber 오리" << endl;
    }
};

MallardDuck(청둥오리)는 기본적으로 날개로 날고(FlyWithWings), “꽥꽥” 우는(QuackQuack) 행동을 가집니다.
RubberDuck(고무오리)는 날지 못하며(FlyNoWay), “삑삑” 소리(SqueakQuack)를 냅니다.

// 실제 사용
int main()
{
    Duck* mallardDuck = new MallardDuck;
    Duck* rubberDuck = new RubberDuck;

    mallardDuck->Display();         // Mallard 오리
    mallardDuck->PerformFly();      // 날개로 납니다.
    mallardDuck->PerformQuack();    // 꽥꽥 소리를 냅니다.

    rubberDuck->Display();          // Rubber 오리
    rubberDuck->PerformFly();       // 날지 못합니다.
    rubberDuck->PerformQuack();     // 삑삑 소리를 냅니다.
    rubberDuck->SetFlyBehavior(new FlyRocketPowered); // 인터페이스 변경
    rubberDuck->PerformFly();       // 로켓 추진으로 엄청나게 날아갑니다!!!

    delete mallardDuck;
    delete rubberDuck;
}

MallardDuckRubberDuck 객체를 생성하고 행동을 실행합니다.

RubberDuck의 비행 행동을 FlyRocketPowered로 변경한 후 다시 PerformFly함수를 실행하면 “로켓 추진으로 엄청나게 날아갑니다!!!”가 출력됩니다.

동적 할당된 객체를 delete하여 메모리 누수를 방지합니다.

DesignPattern 카테고리 내 다른 글 보러가기

댓글남기기