본문 바로가기
Thinking/Concept

전략 패턴(Strategy Pattern) 이해하기

by Dev_카페인 2024. 11. 17.
반응형

전략 패턴(Strategy Pattern) 이해하기

전략 패턴알고리즘을 캡슐화하여 교체가 가능하도록 설계된 행동 디자인 패턴입니다. 이 패턴은 동일한 문제를 해결하기 위한 여러 알고리즘이 있을 때, 이를 개별적인 클래스에 캡슐화하고, 런타임에 필요에 따라 교체하여 사용할 수 있도록 합니다.

전략 패턴은 클라이언트 코드와 알고리즘 구현을 분리함으로써 코드의 유연성과 확장성을 높이는 데 도움을 줍니다. 이번 포스트에서는 전략 패턴의 개념, 구조, 구현 방법, 장단점 및 사용 사례를 구체적인 예제와 함께 살펴보겠습니다.

 

전략 패턴이란?

전략 패턴은 행동(알고리즘)을 정의하고 캡슐화하여, 행동을 런타임에 교체할 수 있도록 설계하는 패턴입니다. 이 패턴을 사용하면 객체는 하나의 행동을 직접 구현하는 대신, 행동을 캡슐화한 전략(Strategy) 객체에 위임합니다.

주요 개념은 행동의 변경이나 확장이 필요할 때, 클라이언트 코드의 수정 없이 전략 객체를 교체하여 이를 구현하는 것입니다.

전략 패턴의 구조

전략 패턴은 다음과 같은 구성 요소로 이루어집니다.

  1. Context (문맥):
    • 클라이언트와 전략 객체 간의 연결점 역할을 하며, 전략 객체를 사용하여 행동을 수행합니다.
  2. Strategy (전략 인터페이스):
    • 알고리즘의 공통 인터페이스를 정의하며, 다양한 전략의 구현체를 포함합니다.
  3. ConcreteStrategy (구체적인 전략):
    • Strategy 인터페이스를 구현하며, 알고리즘의 구체적인 동작을 정의합니다.

전략 (Strategy)

전략 패턴의 구현 방법

예제 코드: 결제 시스템

전략 패턴을 활용하여 **다양한 결제 수단(예: 신용카드, 페이팔, 비트코인)**을 처리하는 예제를 살펴보겠습니다.

// Strategy 인터페이스
public interface IPaymentStrategy
{
    void Pay(int amount);
}

// ConcreteStrategy 클래스: 신용카드 결제
public class CreditCardPayment : IPaymentStrategy
{
    private string _cardNumber;

    public CreditCardPayment(string cardNumber)
    {
        _cardNumber = cardNumber;
    }

    public void Pay(int amount)
    {
        Console.WriteLine($"Paid {amount} using Credit Card ({_cardNumber}).");
    }
}

// ConcreteStrategy 클래스: 페이팔 결제
public class PayPalPayment : IPaymentStrategy
{
    private string _email;

    public PayPalPayment(string email)
    {
        _email = email;
    }

    public void Pay(int amount)
    {
        Console.WriteLine($"Paid {amount} using PayPal ({_email}).");
    }
}

// ConcreteStrategy 클래스: 비트코인 결제
public class BitcoinPayment : IPaymentStrategy
{
    private string _walletAddress;

    public BitcoinPayment(string walletAddress)
    {
        _walletAddress = walletAddress;
    }

    public void Pay(int amount)
    {
        Console.WriteLine($"Paid {amount} using Bitcoin (Wallet: {_walletAddress}).");
    }
}

// Context 클래스
public class PaymentContext
{
    private IPaymentStrategy _paymentStrategy;

    public void SetPaymentStrategy(IPaymentStrategy paymentStrategy)
    {
        _paymentStrategy = paymentStrategy;
    }

    public void ExecutePayment(int amount)
    {
        if (_paymentStrategy == null)
        {
            Console.WriteLine("No payment strategy set.");
            return;
        }
        _paymentStrategy.Pay(amount);
    }
}

// Client 코드
class Program
{
    static void Main()
    {
        PaymentContext paymentContext = new PaymentContext();

        // 신용카드 결제
        paymentContext.SetPaymentStrategy(new CreditCardPayment("1234-5678-9876-5432"));
        paymentContext.ExecutePayment(100);

        // 페이팔 결제
        paymentContext.SetPaymentStrategy(new PayPalPayment("user@example.com"));
        paymentContext.ExecutePayment(200);

        // 비트코인 결제
        paymentContext.SetPaymentStrategy(new BitcoinPayment("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"));
        paymentContext.ExecutePayment(300);
    }
}

실행 결과

 
Paid 100 using Credit Card (1234-5678-9876-5432).
Paid 200 using PayPal (user@example.com).
Paid 300 using Bitcoin (Wallet: 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa).

코드 설명

  1. Strategy:
    • IPaymentStrategy 인터페이스는 결제 동작(Pay)을 정의합니다.
  2. ConcreteStrategy:
    • CreditCardPayment, PayPalPayment, BitcoinPayment 클래스는 각각 신용카드, 페이팔, 비트코인 결제 로직을 구현합니다.
  3. Context:
    • PaymentContext 클래스는 결제 전략을 설정(SetPaymentStrategy)하고, 실행(ExecutePayment)합니다.
  4. Client:
    • 클라이언트는 PaymentContext를 사용하여 런타임에 결제 전략을 설정하고 실행합니다.

 

전략 패턴의 장단점

장점

  1. 유연성:
    • 알고리즘을 클래스 단위로 캡슐화하여, 런타임에 동적으로 교체할 수 있습니다.
  2. 단일 책임 원칙 준수:
    • 알고리즘 구현을 별도의 클래스에 캡슐화하여, 클라이언트와 알고리즘 간의 책임을 분리합니다.
  3. 확장성:
    • 새로운 알고리즘을 추가해도 기존 코드를 수정할 필요가 없으므로 확장성이 뛰어납니다.

단점

  1. 클래스 수 증가:
    • 각 알고리즘마다 별도의 클래스가 필요하므로, 클래스 수가 증가할 수 있습니다.
  2. 클라이언트의 설정 책임:
    • 클라이언트가 적절한 전략을 선택하고 설정해야 하므로, 사용법이 복잡해질 수 있습니다.

 

언제 전략 패턴을 사용해야 할까?

전략 패턴은 다음과 같은 상황에서 유용합니다.

  1. 여러 알고리즘이 필요한 경우:
    • 동일한 문제를 해결하기 위해 다양한 알고리즘이 존재하고, 이를 런타임에 교체해야 할 때.
  2. 알고리즘의 구현이 자주 변경될 가능성이 있는 경우:
    • 알고리즘 로직이 자주 변경되거나 확장될 가능성이 높은 경우.
  3. 클라이언트 코드와 알고리즘을 분리해야 할 때:
    • 클라이언트 코드에서 알고리즘 구현을 독립적으로 관리해야 할 때.

 

전략 패턴과 관련 패턴 비교

전략 패턴은 알고리즘 캡슐화와 동적 교체에 초점을 둡니다. 다른 패턴과의 차이를 살펴보겠습니다.

  • 상태 패턴:
    • 객체의 상태에 따라 동작을 변경하며, 상태 전환 로직이 포함됩니다. 전략 패턴은 상태 전환이 없고, 알고리즘 자체에 집중합니다.
  • 데코레이터 패턴:
    • 객체에 새로운 기능을 동적으로 추가합니다. 전략 패턴은 기존 동작을 교체하거나 선택적으로 사용합니다.
  • 템플릿 메서드 패턴:
    • 알고리즘의 구조를 정의하고, 일부 세부 사항을 서브클래스에서 구현합니다. 전략 패턴은 알고리즘 전체를 독립적인 클래스로 캡슐화합니다.

 

실무에서 전략 패턴 활용하기

전략 패턴은 다음과 같은 실무 상황에서 활용됩니다.

  1. 결제 시스템:
    • 다양한 결제 수단(카드, 페이팔, 비트코인 등)을 처리하는 로직 구현.
  2. AI 게임 캐릭터:
    • 게임 캐릭터의 행동(공격, 방어, 이동)을 상황에 따라 동적으로 변경.
  3. 데이터 정렬:
    • 다양한 정렬 알고리즘(버블 정렬, 퀵 정렬 등)을 선택적으로 적용.
  4. 파일 압축:
    • ZIP, RAR, GZIP 등 다양한 파일 압축 알고리즘을 동적으로 교체.

 

마무리하며

전략 패턴은 다양한 알고리즘을 캡슐화하여 런타임에 동적으로 교체할 수 있는 강력한 디자인 패턴입니다. 특히, 알고리즘이 자주 변경되거나 확장 가능한 시스템에서 큰 효과를 발휘합니다.

알고리즘 캡슐화와 런타임 교체가 필요한 시스템을 설계할 때, 전략 패턴을 활용해보세요. 이를 통해 코드의 유연성과 확장성을 크게 향상시킬 수 있습니다.

반응형