C++/개념정리

전략패턴

jeongchanhyo 2025. 2. 26. 20:52

전략패턴이란?

알고리즘을 정의하고, 알고리즘을 각각의 전략으로 캡슐화하여 클라이언트에서 독립적으로 사용할 수 있도록 하는 패턴이다.

주요 구성 요소

  • 전략 인터페이스(Strategy Interface): 다양한 전략들이 구현해야 하는 공통 인터페이스를 정의합니다.
  • 구체적인 전략(Concrete Strategy): 전략 인터페이스를 구현하는 클래스들로, 각기 다른 알고리즘을 구체적으로 정의합니다.
  • 컨텍스트(Context): 전략을 사용하는 클라이언트 클래스입니다. 이 클래스는 전략 인터페이스를 통해 다양한 알고리즘을 사용할 수 있습니다.

이런 구조 덕분에 런타임 중에 알고리즘을 변경한다던가, 코드 구성 시 조건문(if else)를 줄일 수 있다던가, 새로운 전략을 추가하기 쉽다는 큰 장점들을 가지고 있다.

 

전략요소 예시

예시로는 음식을 결제할 때 1000원할인, 30%할인 등 할인의 종류가 다양하다.

전략 인터페이스 추상 클래스

class IDiscountStrategy 
{
public:
    virtual double ApplyDiscount(double amount) = 0;
        virtual ~IDiscountStrategy() = default;
};

할인 함수와 소멸자를 만들어준다.

여기서 할인 함수는 순수 가상 함수로 만들어 모든 자식클래스에서 반드시 구현 하도록 한다.

구체적인 전략

//특정액 할인 전략
class FixedDiscountStrategy : public IDiscountStrategy 
{
    double discountAmount;
public:
    FixedDiscountStrategy(double amount) : discountAmount(amount) {}
    double ApplyDiscount(double amount) override 
    {
        return amount - discountAmount;
    }
};

//백분율 할인 전략
class PercentageDiscountStrategy : public IDiscountStrategy 
{
    double percentage;
public:
    PercentageDiscountStrategy(double percent) : percentage(percent) {}
    double ApplyDiscount(double amount) override 
    {
        return amount * (1 - (percentage / 100));
    }
};

//할인 없음 전략
class NoDiscountStrategy : public IDiscountStrategy 
{
public:
    double ApplyDiscount(double amount) override 
    {
        return amount;
    }
};

간단하다. 그냥 각 전략에 맞춰 하나는 빼기(-) 하나는 백분율에 맞춰 나눠서 빼주기, 하나는 아무것도 없이 리턴 해주면 된다. 함수 자체는 정말 쉽다.

중요한건 IDiscountStrategy를 상속받는 자식 클래스로 전략을 하나씩 만드는것이다.

 

구분 후 실행해주는(오더) 클래스 구현

class Order 
{
    unique_ptr<IDiscountStrategy> discountStrategy;
    double totalAmount = 0;

public:
    Order(unique_ptr<IDiscountStrategy> strategy)
        : discountStrategy(move(strategy)) 
    {
    }

    void AddItem(double price) 
    {
        totalAmount += price;
    }

    double CalculateTotal() 
    {
        return discountStrategy->ApplyDiscount(totalAmount);
    }
};

이 오더 클래스는 인터페이스 클래스(IDiscountStrategy)를 상속받지 않는다

우선 맴버 변수를 보겠다. 

unique_ptr<IDiscountStrategy> discountStrategy;
double totalAmount = 0;

discountStrategy는 인터페이스 안에서 구체적인 할인 전략 객체를 가리키는 스마트 포인터다.

totalAmount는 주문한 물건의 총 값을 저장하는 변수다.(결제할 가격 X)

 

그 다음 메서드는

Order(unique_ptr<IDiscountStrategy> strategy)
    : discountStrategy(move(strategy)) 
{
}

void AddItem(double price) 
{
    totalAmount += price;
}

double CalculateTotal() 
{
    return discountStrategy->ApplyDiscount(totalAmount);
}

생성자 - unique_ptr<IDiscountStrategy> 타입의 매개변수를 받아 초기화. move(strategy)를 사용하여 소유권을 이동시키므로, Order 객체가 생성된 후에는 더 이상 외부에서 해당 전략에 접근할 수 없다.

 

AddItem - price를 넣고 그냥 그 값을 지불할 총 가격에 넣는것이다.

 

CalculateTotal - 현재 총 금액에 대해 할인 전략을 적용하여 최종 금액을 계산합니다. discountStrategy를 통해 ApplyDiscount 메서드를 호출하여 할인된 금액을 반환한다.

 

사용예시

int main() {
    // 특정액 할인 전략
    Order order1(make_unique<FixedDiscountStrategy>(3000));
    order1.AddItem(5000);
    order1.AddItem(8000);
    cout << "첫 번째 주문 총액: " << order1.CalculateTotal() << "원" << endl;

    // 백분율 할인 전략
    Order order2(make_unique<PercentageDiscountStrategy>(20));
    order2.AddItem(10000);
    order2.AddItem(7500);
    cout << "두 번째 주문 총액: " << order2.CalculateTotal() << "원" << endl;

    // 할인 없음
    Order order3(std::make_unique<NoDiscountStrategy>());
    order3.AddItem(13000);
    cout << "세 번째 주문 총액: " << order3.CalculateTotal() << "원" << endl;
}

Order 클래스의 order1이라는 녀석을 만들어주고 그녀석은

make_unique<FixedDiscountStrategy>(3000) = 즉 3000원을 깎아주는 할인쿠폰을 쓴다고 보면 된다.

그리고 

    order1.AddItem(5000);
    order1.AddItem(8000);
이것들은 장바구니에 5000원짜리 음식, 8000원짜리 음식을 담는거라고 보면 된다.

이렇게 하여 실행을 해보면?

 

값이 아주 잘 나온다.

결론

전략패턴은 하나의 이름으로 다양한 패턴을 사용할 때에 용이하며, 간단한것을 만들 때에는 만드는 구조나 설계에 있어 복잡해질 수 있으므로 잘 구분해서 사용하면 정말 좋은 구조를 만들어 여러가지 기능을 적용할 수 있다.

'C++ > 개념정리' 카테고리의 다른 글

템플릿 메서드 패턴  (2) 2025.03.07
상태 패턴  (0) 2025.03.05
[디버그 입문]  (1) 2025.02.11
개념정리[연산자(&*)]  (0) 2025.02.07
C++[코드변경 깃허브 적용]  (0) 2025.01.13