[디자인패턴] 전략 패턴 (Strategy Pattern)
April 07, 2024
전략 패턴(Strategy Pattern)은 알고리즘을 캡슐화하여, 동적으로 알고리즘을 교체 가능하도록 하는 디자인 패턴입니다.
예를 들어, 결제 방법이 여러가지가 있다고 가정하면, 상황에 따라서 어쩔 때는 카드로 결제하고, 어쩔 때는 현금으로 결제할 수 있는데요.
다양한 결제 방법을 객체들로 만들고, 결제를 처리하는 곳에서 결제 방법을 동적으로 교체하여 어떤 알고리즘을 수행시킬 수 있습니다.
따라서, 어떤 작업을 수행할 때 다양한 알고리즘이 존재하며 상황에 따라 특정 알고리즘을 사용하여 처리해야한다면 전략 패턴이 사용될 수 있습니다.
전략 패턴
전략 패턴은 다음과 같습니다.
- 다양한 알고리즘(family 알고리즘 - 비슷한 목적으로 사용되는)을 정의
- 각각의 알고리즘을 캡슐화
- 각각의 알고리즘을 동적으로 변경하여 수행시킬 수 있음
클래스 다이어그램을 보면, Strategy(알고리즘)를 추상화하고 상속을 통해 알고리즘을 구현합니다. 즉, 상속을 통해 확장을 합니다.
전략 패턴 특징
- 유연성: 실행 중에 알고리즘을 쉽게 변경할 수 있습니다.
- 재사용성: 다양한 상황에서 같은 알고리즘을 재사용할 수 있습니다.
- 확장성: 새로운 전략을 쉽게 추가할 수 있습니다.
- SOLID 원칙: OCP(개방/폐쇄 원칙)과 DIP(의존성 역전 원칙)을 따릅니다.
- 상속을 통해 알고리즘 확장
- (단점) 전략 클래스의 수 증가: 전략 클래스가 많아지면 관리해야 할 클래스의 수가 늘어납니다.
예제 코드 (Java)
전체적으로 코드를 보면
- ShoppingCart에 Item들을 추가할 수 있다.
ShoppingCart
는 결제를 할 수 있는데,CreditCardStrategy
와PaypalStrategy
중에 골라서 결제할 수 있다.
전략 패턴 측면에서 살펴보면,
- 전략 패턴은 결제 알고리즘을
PaymentStrategy
로 추상화하였으며, 특히 pay() 메소드로 추상화했다. - 결제 알고리즘의 실제 구현은
CreditCardStrategy
와PaypalStrategy
가 있다. pay()를 해당 알고리즘에 맞게 구현하였다.
import java.util.ArrayList;
public class Example {
// 전략 인터페이스
interface PaymentStrategy {
void pay(int amount);
}
// 전략 1
static class CreditCardStrategy implements PaymentStrategy {
private String name;
private String cardNumber;
public CreditCardStrategy(String name, String cardNum) {
this.name = name;
this.cardNumber = cardNum;
}
@Override
public void pay(int amount) {
System.out.println(amount + " paid with credit/debit card");
}
}
// 전략 2
static class PaypalStrategy implements PaymentStrategy {
private String emailId;
public PaypalStrategy(String email) {
this.emailId = email;
}
@Override
public void pay(int amount) {
System.out.println(amount + " paid using Paypal.");
}
}
// 아이템 클래스
static class Item {
private String name;
private int price;
public Item(String name, int cost) {
this.name = name;
this.price = cost;
}
public int getPrice() {
return price;
}
}
// 쇼핑 카트 클래스
static class ShoppingCart {
private ArrayList<Item> items;
private PaymentStrategy paymentStrategy;
public ShoppingCart() {
this.items = new ArrayList<>();
}
public void addItem(Item item) {
this.items.add(item);
}
public int calculateTotal() {
int sum = 0;
for (Item item : items) {
sum += item.getPrice();
}
return sum;
}
public void setPaymentStrategy(PaymentStrategy strategy) {
this.paymentStrategy = strategy;
}
public void checkout() {
int amount = calculateTotal();
paymentStrategy.pay(amount);
}
}
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
// Item 객체 생성
Item item1 = new Item("item1", 10);
Item item2 = new Item("item2", 40);
// ShoppingCart에 Item 추가
cart.addItem(item1);
cart.addItem(item2);
// 신용카드로 결제
cart.setPaymentStrategy(new CreditCardStrategy("John Doe", "12345"));
cart.checkout();
// PayPal로 결제
cart.setPaymentStrategy(new PaypalStrategy("johndoe@example.com"));
cart.checkout();
}
}
Output:
50 paid with credit/debit card
50 paid using Paypal.
예제 코드에 대한 UML
위 예제 코드에 대한 UML을 그려보면 아래와 같다.
- 두개의 전략 클래스는 PaymentStrategy를 상속하여 확장한다.
- ShoppingCart에서 알고리즘을 교체해서 사용할 수 있으며, 인터페이스를 통해 호출한다.(Delegation & Composition)