Skip to content
Kang Log
Go back

전략패턴

Updated:

변화하는 부분을 찾기

바뀌는 부분은 따로 뽑아서 캡슐화한다. 그러면 나중에 바뀌지 않는 부분에는 영향을 미치지 않고 그 부분만 고치거나 확장할 수 있다.

예를 들어

오리에 대해 서브클래스를 구현할때 Flyable, Quakable 이라는 행동의 구현체를 상속을 받아서 구현을 한다면, 모형 오리는 Quackable하지 않으므로 해당 클래스에서 따로 오버라이딩할 함수를 작성해야한다. 그리고 수정사항이 생긴다면 모든 클래스에 대한 오버라이딩 함수를 다 수정해줘야하는 불상사가 발생한다.

따라서

디자인 원칙

  1. 애플리케이션에서 달라지는 부분을 찾아내고, 달라지지 않는 부분과 분리한다.
  2. 구현보다는 인터페이스에 맞춰서 프로그래밍한다

2026-06-26-001

C++로 알아보는 간단한 예시

class PaymentStategy {
public:
  virtual void pay(int amount) = 0;
  virtual ~PaymentStategy() = default;
};

결제수단이 변경되는 요소로 찾았다면 이에 대한 인터페이스를 작성하고 캡슐화해야한다.


// 구체 전략 1
class CardPayment :public PaymentStategy {
public:
  void pay(int amount) {
    cout << "카드로 " << amount << "원 결제\n";
  }
};

// 구체 전략 2
class KakaoPayment :public PaymentStategy {
public:
  void pay(int amount) {
    cout << "카카오페이로 " << amount << "원 결제\n";
  }
};

// 구체 전략 3
class CashPayment :public PaymentStategy {
public:
  void pay(int amount) {
    cout << "현금으로 " << amount << "원 결제\n";
  }
};

변화하는 요소의 인터페이스에 대한 구체적인 구현체를 만들어서 관리하면 변경 시 손쉽게 수정할 수 있다.

// 전략을 사용하는 객체
class ShoppingCart {
private:
  unique_ptr<PaymentStategy> payment;
public:
  void setPaymentStrategy(unique_ptr<PaymentStategy> payment) {
    this->payment = move(payment);
  }

  void checkout(int amount) {
    if (!payment) {
      cout << "아직 결제수단이 결정되지 않았습니다\n";
      return;
    }
    payment->pay(amount);
  }
};

캡슐화된 행동을 사용할때에는 상속이 아닌 composition(구성) 방식을 사용하여 언제든 갈아끼워넣을 수 있게 설계한다.

상속이나 인터페이스 구현을 하게 되면 Shopping Cart is-a PaymentStrategy 관계가 되고, 구성방식을 하면 Shopping Cart has-a PaymentStrategy 관계가 되는데 의미론적으로 has-a 관게가 올바르다. 또한 ShoppingCart가 전략 인터페이스를 직접 구현하게 되면 결제 로직이 장바구니 로직에 결합되어, 이미 만들어둔 구체 전략 구현체를 자유롭게 교체하거나 조합하기 어려워진다. 반면 구성 방식을 사용하면 기존 전략 객체를 그대로 주입하고 교체할 수 있어 확장과 유지보수가 쉬워진다.

int main(void) {
  ShoppingCart cart;
  cart.setPaymentStrategy(make_unique<CardPayment>());
  cart.checkout(10000);

  cart.setPaymentStrategy(make_unique<KakaoPayment>());
  cart.checkout(20000);

  cart.setPaymentStrategy(make_unique<CashPayment>());
  cart.checkout(30000);

  return 0;
}


--- 실행결과 --- 
카드로 10000원 결제
카카오페이로 20000원 결제
현금으로 30000원 결제

위 예시처럼 직접 전략을 선택해 넣는 방식으로 동적으로 설계할 수도 있고, 생성자나 팩토리 함수를 통해 필요한 전략을 미리 넣어두는 보다 정적인 방법으로 설계할 수도 있다.


Share this post on:

Previous Post
0. 학습의 종류
Next Post
코드컨벤션