JAVA로 구현하는 알고리즘과 디자인 패턴 공부: 전략 패턴 (Strategy)
1. 전략 패턴이란? ⚔️
개념: 다양한 전략(알고리즘)들을 각각의 클래스로 캡슐화하고, 이들을 동적으로 교체하여 사용할 수 있게 만드는 패턴입니다. 즉, 행위(전략) 자체를 객체로 만들어 컨텍스트 객체에 주입하는 방식입니다.
게임 캐릭터가 상황에 따라 칼, 활, 마법 지팡이 등 다양한 무기(전략)를 바꿔가며 공격하는 것을 생각하면 쉽습니다. 캐릭터(컨텍스트)는 공격한다는 행위 자체는 변하지 않지만, 어떤 무기(전략 객체)를 손에 드느냐에 따라 공격 방식이 완전히 달라집니다.
2. 왜 사용할까요?
- 전략의 손쉬운 교체: if-else 나 switch 문으로 가득 찬 코드를 피할 수 있습니다. 새로운 전략이 추가되더라도 기존 코드를 수정할 필요 없이 새로운 전략 클래스만 추가하면 됩니다.
- 개방-폐쇄 원칙 (OCP) 충족: 새로운 전략의 추가에는 열려 있고(Open for extension), 기존 컨텍스트 코드의 수정에는 닫혀 있습니다(Closed for modification).
- 관심사 분리: 컨텍스트(캐릭터)는 '어떻게' 공격하는지 알 필요 없이 '공격한다'는 자신의 주요 임무에만 집중할 수 있습니다. 구체적인 공격 방식은 각 전략 클래스가 알아서 책임집니다.
3. Java로 구현하는 방법
시나리오: 쇼핑몰의 결제 시스템을 만들어 보겠습니다. 사용자는 신용카드, 카카오페이, 네이버페이 등 다양한 결제 방법(전략)을 선택할 수 있어야 합니다.
1단계: 전략(Strategy) 인터페이스 정의
모든 결제 전략들이 구현해야 할 공통 규약입니다.
// 모든 결제 전략이 구현해야 할 공통 인터페이스
public interface PaymentStrategy {
void pay(int amount);
}
2단계: 구체적인 전략(Concrete Strategy) 클래스 구현
실제 결제 방식을 구현하는 클래스들입니다.
// 전략 1: 신용카드 결제
public class CreditCardStrategy implements PaymentStrategy {
private String name;
private String cardNumber;
public CreditCardStrategy(String name, String cardNumber) {
this.name = name;
this.cardNumber = cardNumber;
}
@Override
public void pay(int amount) {
System.out.println(amount + "원을 신용카드(" + cardNumber + ")로 결제했습니다.");
}
}
// 전략 2: 카카오페이 결제
public class KakaoPayStrategy implements PaymentStrategy {
private String email;
public KakaoPayStrategy(String email) {
this.email = email;
}
@Override
public void pay(int amount) {
System.out.println(amount + "원을 카카오페이(" + email + ")로 결제했습니다.");
}
}
3단계: 컨텍스트(Context) 클래스 구현
전략을 사용하는 주체입니다. 컨텍스트는 전략 인터페이스 타입의 멤버 변수를 가지며, 생성자나 별도의 메서드를 통해 구체적인 전략 객체를 주입받습니다.
// 전략을 사용하는 컨텍스트: 쇼핑 카트
public class ShoppingCart {
private int amount;
public ShoppingCart(int amount) {
this.amount = amount;
}
// 외부에서 주입된 결제 전략을 사용하여 결제를 수행한다.
// ShoppingCart는 구체적인 결제 방식(신용카드, 카카오페이 등)을 전혀 모른다.
public void pay(PaymentStrategy paymentMethod) {
paymentMethod.pay(amount);
}
}
4단계: 클라이언트 코드에서 사용하기
클라이언트는 어떤 전략을 사용할지 결정하고, 해당 전략 객체를 생성하여 컨텍스트(쇼핑 카트)에 전달합니다.
public class Store {
public static void main(String[] args) {
// 1. 10,000원짜리 상품을 카트에 담음
ShoppingCart cart = new ShoppingCart(10000);
// 2. 신용카드로 결제하기로 결정 (전략 선택)
PaymentStrategy creditCard = new CreditCardStrategy("홍길동", "1234-5678-9012-3456");
// 컨텍스트에 전략을 주입하여 실행
cart.pay(creditCard);
System.out.println("---");
// 3. 15,000원짜리 상품을 다른 카트에 담음
ShoppingCart cart2 = new ShoppingCart(15000);
// 4. 이번엔 카카오페이로 결제하기로 결정 (전략 교체)
PaymentStrategy kakaoPay = new KakaoPayStrategy("gildong@example.com");
// 컨텍스트에 다른 전략을 주입하여 실행
cart2.pay(kakaoPay);
// 만약 '네이버페이' 전략이 추가되어도 Store나 ShoppingCart 코드는 전혀 수정할 필요가 없다.
}
}
4. 전략 패턴과 실제 사례
- Java Collections.sort(): sort() 메서드는 정렬할 리스트와 함께 Comparator라는 객체를 인자로 받을 수 있습니다. 이 Comparator가 바로 정렬 방식을 정의하는 전략 객체입니다. 오름차순, 내림차순, 또는 특정 필드 기준 등 다양한 정렬 전략을 Comparator로 만들어 전달하면 sort() 메서드는 그 전략에 따라 동작합니다.
- 스프링 프레임워크: 스프링의 많은 부분에서 전략 패턴을 찾아볼 수 있습니다. 예를 들어, 보안을 처리하는 Spring Security에서 사용자가 로그인할 때 인증을 처리하는 방식(AuthenticationStrategy), 리소스를 찾는 방식(ResourceLoader) 등 수많은 기능들이 전략 패턴으로 구현되어 있어 개발자가 원하는 구현체로 쉽게 교체할 수 있습니다.
5. 문제 제시 및 답변 💡
문제: 배송비를 계산하는 시스템을 만들려고 합니다. 배송비는 배송 방법에 따라 다르게 책정됩니다. 예를 들어, '일반 배송'은 3,000원, '특급 배송'은 6,000원, '해외 배송'은 무게에 따라 계산됩니다.
전략 패턴을 사용하여 이 배송비 계산 시스템을 어떻게 설계할 수 있을까요?
답변:
결제 시스템 예제와 매우 유사하게 설계할 수 있습니다.
- ShippingStrategy 인터페이스: 모든 배송비 계산 전략이 구현해야 할 공통 인터페이스를 만듭니다. calculateFee(Item item) 같은 메서드를 가집니다.
- 구체적인 전략 클래스:
- StandardShippingStrategy: calculateFee 메서드에서 항상 3000을 반환합니다.
- ExpressShippingStrategy: calculateFee 메서드에서 항상 6000을 반환합니다.
- OverseasShippingStrategy: calculateFee 메서드에서 인자로 받은 Item 객체의 무게(weight)를 기반으로 배송비를 계산하여 반환합니다.
- ShippingCalculator 컨텍스트 클래스: calculate(ShippingStrategy strategy, Item item) 같은 메서드를 가집니다. 이 메서드는 전달받은 strategy 객체의 calculateFee 메서드를 호출하여 결과를 반환합니다.
이렇게 설계하면, 나중에 '산간지역 배송' 같은 새로운 배송비 정책이 추가되더라도 MountainShippingStrategy 클래스 하나만 새로 만들면 되므로, 기존 코드를 전혀 건드리지 않고 시스템을 유연하게 확장할 수 있습니다.
'프로그래밍 > 알고리즘&자료구조&패턴' 카테고리의 다른 글
| 디자인패턴 - 커맨드 패턴 (1) | 2025.08.18 |
|---|---|
| 디자인패턴 - 옵저버 패턴 (2) | 2025.08.18 |
| 디자인패턴 - 컴포지트 패턴 (2) | 2025.08.18 |
| 디자인패턴 - 프록시 패턴 (1) | 2025.08.18 |
| 디자인패턴 - 퍼사드 패턴 (2) | 2025.08.18 |