전략 패턴(Strategy Pattern)은 행위 디자인 패턴 중 하나로, 객체의 행위를 변경할 수 있도록 하는 패턴입니다. 이 패턴은 실행 중에 알고리즘을 선택할 수 있게 해주며, 특정 기능을 수행하는 다양한 방법을 정의하고, 그 중 하나를 런타임에 선택하여 사용할 수 있도록 해줍니다. 즉, 상속 대신 객체의 구성(Composition)을 활용하여 동작을 변경할 수 있게 하는 패턴입니다.
전략 패턴은 알고리즘을 각각의 클래스(전략)로 캡슐화하고, 이 알고리즘들을 상호 교체 가능하도록 만드는 패턴입니다. 클라이언트는 동적으로(런타임에) 사용할 알고리즘을 선택할 수 있으며, 알고리즘을 사용하는 코드는 변경되지 않습니다.
이 패턴을 사용하면 클래스의 행위를 변경할 때 코드의 수정 없이 알고리즘을 쉽게 교체할 수 있기 때문에 유연성과 확장성을 높일 수 있습니다.
전략 패턴은 주로 세 가지 구성 요소로 이루어져 있습니다.
+------------------+ +------------------+
| Context |<--------+ | Strategy |
+------------------+ | +------------------+
| strategy:Strategy | | | execute() |
+------------------+ | +------------------+
| executeStrategy() | |
+------------------+ |
+--------+
|
+------------------------------------+
| ConcreteStrategyA |
| ConcreteStrategyB |
| ConcreteStrategyC |
+------------------------------------+
다음은 Java에서 전략 패턴을 활용한 간단한 예시입니다. 다양한 할인 정책을 적용하는 예를 생각해 보겠습니다.
// Strategy 인터페이스
interface DiscountStrategy {
double applyDiscount(double price);
}
// Concrete Strategy 1: 학생 할인
class StudentDiscount implements DiscountStrategy {
@Override
public double applyDiscount(double price) {
return price * 0.8; // 20% 할인
}
}
// Concrete Strategy 2: VIP 할인
class VIPDiscount implements DiscountStrategy {
@Override
public double applyDiscount(double price) {
return price * 0.7; // 30% 할인
}
}
// Concrete Strategy 3: 일반 할인 없음
class NoDiscount implements DiscountStrategy {
@Override
public double applyDiscount(double price) {
return price; // 할인 없음
}
}
// Context 클래스
class PriceCalculator {
private DiscountStrategy discountStrategy;
// 전략을 동적으로 변경 가능
public void setDiscountStrategy(DiscountStrategy discountStrategy) {
this.discountStrategy = discountStrategy;
}
// 전략을 이용해 가격 계산
public double calculatePrice(double price) {
return discountStrategy.applyDiscount(price);
}
}
// 클라이언트 코드
public class Main {
public static void main(String[] args) {
PriceCalculator calculator = new PriceCalculator();
// 학생 할인 적용
calculator.setDiscountStrategy(new StudentDiscount());
System.out.println("학생 할인 가격: " + calculator.calculatePrice(100)); // 80.0
// VIP 할인 적용
calculator.setDiscountStrategy(new VIPDiscount());
System.out.println("VIP 할인 가격: " + calculator.calculatePrice(100)); // 70.0
// 할인 없음
calculator.setDiscountStrategy(new NoDiscount());
System.out.println("할인 없음 가격: " + calculator.calculatePrice(100)); // 100.0
}
}
설명:
템플릿 메서드 패턴과 전략 패턴은 비슷한 개념처럼 보일 수 있지만, 사용 방식에서 차이가 있습니다:
답: 전략 패턴에서 알고리즘(전략)이 많아지면 클래스를 관리하기 어려워질 수 있습니다. 이를 효율적으로 관리하려면 다음과 같은 방법을 고려할 수 있습니다:
답: 전략 패턴을 사용하여 여러 개의 결제 방식을 구현할 때는 각 결제 방식을 별도의 전략으로 캡슐화하여 관리하면 됩니다. 각 결제 방식(예: 신용카드, 페이팔, 은행 계좌)은 PaymentStrategy라는 인터페이스를 구현하는 구체적인 전략 클래스가 될 수 있습니다. 다음과 같이 설계할 수 있습니다:
interface PaymentStrategy {
void pay(double amount);
}
class CreditCardPayment implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("신용카드로 " + amount + "원 결제합니다.");
}
}
class PayPalPayment implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("PayPal로 " + amount + "원 결제합니다.");
}
}
class PaymentContext {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void executePayment(double amount) {
paymentStrategy.pay(amount);
}
}
public class Main {
public static void main(String[] args) {
PaymentContext context = new PaymentContext();
// 신용카드 결제
context.setPaymentStrategy(new CreditCardPayment());
context.executePayment(5000);
// PayPal 결제
context.setPaymentStrategy(new PayPalPayment());
context.executePayment(10000);
}
}
이렇게 하면 새로운 결제 방식을 추가할 때는 기존 코드를 변경하지 않고 새로운 전략 클래스를 추가하기만 하면 됩니다.
답: Context 객체가 전략을 자동으로 선택하게 하려면, 전략 선택을 외부 요인에 따라 동적으로 결정하는 메커니즘을 추가할 수 있습니다. 다음과 같은 방법들이 있습니다:
public void selectStrategy(double amount) {
if (amount > 10000) {
setPaymentStrategy(new CreditCardPayment());
} else {
setPaymentStrategy(new PayPalPayment());
}
}
설정 파일 또는 데이터베이스에 저장된 전략 정보 사용: 외부의 설정 파일이나 데이터베이스에서 사용자가 선호하는 전략을 불러와서 적용할 수 있습니다. 이를 통해 사용자의 환경에 맞는 전략을 자동으로 적용하게 할 수 있습니다.
DI 프레임워크 사용: Spring과 같은 의존성 주입(DI) 프레임워크를 활용하여 전략 객체를 컨텍스트에 자동으로 주입할 수 있습니다. 사용자는 별도로 전략을 설정할 필요 없이, 프레임워크가 런타임에 적절한 전략을 선택하여 주입하게 할 수 있습니다.
팩토리 패턴과 결합: 팩토리 클래스를 사용하여 Context에서 사용할 전략을 결정할 수 있습니다. 예를 들어, PaymentStrategyFactory라는 클래스를 만들어 조건에 맞는 전략을 반환하도록 할 수 있습니다.