전략패턴이란?
알고리즘을 정의하고, 알고리즘을 각각의 전략으로 캡슐화하여 클라이언트에서 독립적으로 사용할 수 있도록 하는 패턴이다.
주요 구성 요소
- 전략 인터페이스(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 |