전략 패턴(Strategy Pattern)은 알고리즘을 캡슐화하여, 동적으로 알고리즘을 교체 가능하도록 하는 디자인 패턴입니다.

예를 들어, 결제 방법이 여러가지가 있다고 가정하면, 상황에 따라서 어쩔 때는 카드로 결제하고, 어쩔 때는 현금으로 결제할 수 있는데요.

다양한 결제 방법을 객체들로 만들고, 결제를 처리하는 곳에서 결제 방법을 동적으로 교체하여 어떤 알고리즘을 수행시킬 수 있습니다.

따라서, 어떤 작업을 수행할 때 다양한 알고리즘이 존재하며 상황에 따라 특정 알고리즘을 사용하여 처리해야한다면 전략 패턴이 사용될 수 있습니다.

전략 패턴

전략 패턴은 다음과 같습니다.

  • 다양한 알고리즘(family 알고리즘 - 비슷한 목적으로 사용되는)을 정의
  • 각각의 알고리즘을 캡슐화
  • 각각의 알고리즘을 동적으로 변경하여 수행시킬 수 있음

클래스 다이어그램을 보면, Strategy(알고리즘)를 추상화하고 상속을 통해 알고리즘을 구현합니다. 즉, 상속을 통해 확장을 합니다.

전략 패턴 클래스 다이어그램

전략 패턴 특징

  • 유연성: 실행 중에 알고리즘을 쉽게 변경할 수 있습니다.
  • 재사용성: 다양한 상황에서 같은 알고리즘을 재사용할 수 있습니다.
  • 확장성: 새로운 전략을 쉽게 추가할 수 있습니다.
  • SOLID 원칙: OCP(개방/폐쇄 원칙)과 DIP(의존성 역전 원칙)을 따릅니다.
  • 상속을 통해 알고리즘 확장
  • (단점) 전략 클래스의 수 증가: 전략 클래스가 많아지면 관리해야 할 클래스의 수가 늘어납니다.

예제 코드 (Java)

전체적으로 코드를 보면

  • ShoppingCart에 Item들을 추가할 수 있다.
  • ShoppingCart는 결제를 할 수 있는데, CreditCardStrategyPaypalStrategy 중에 골라서 결제할 수 있다.

전략 패턴 측면에서 살펴보면,

  • 전략 패턴은 결제 알고리즘을 PaymentStrategy로 추상화하였으며, 특히 pay() 메소드로 추상화했다.
  • 결제 알고리즘의 실제 구현은 CreditCardStrategyPaypalStrategy가 있다. 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)

예제 코드 UML