전략패턴(Strategy pattern)
- 객체가 할 수 있는 행위들을 전략(strategy)으로 만들어두고, 동적으로 행위의 수정이 필요한 경우 전략을 바꾸는 것만으로 수정이 가능하도록 만든 패턴이다.
class VendingMachine {
pay() {
console.log("cash pay!")
}
}
위의 코드는 VendingMachine class 함수가 존재하고 pay 메서드 안에 "cash pay!"가 호출되게끔 구현되어있다.
만약 여기서 결제 방식이 cash가 아닌 card로 변경된다면, 메소드 내에 있는 코드 수정이 불가피해 보인다. 이는 OOP 설계 원칙 중 하나인 OCP(open close principle: 수정에는 닫혀있다는 원칙)를 위배하게 된다. 또한 시스템이 커져서 확장 될 경우 pay 메소드 안에 코드량이 증가하게 된다. 이는 내부 로직이 복잡해지게 되어 연동되는 시스템에도 영향을 끼칠 수 있는 요인이 된다.
따라서 이러한 문제를 해결하기 위해 전략패턴을 도입해 아래의 코드와 같이 해결할 수 있다.
interface PaymentStrategy {
pay(): void;
}
class CardPaymentStrategy implements PaymentStrategy {
pay(): void {
console.log("card pay!");
}
}
class CashPaymentStrategy implements PaymentStrategy {
pay(): void {
console.log("cash pay!");
}
}
class VendingMachine {
private paymentStrategy: PaymentStrategy;
setPaymentStrategy(paymentStrategy: PaymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
pay() {
this.paymentStrategy.pay();
}
}
CardPaymentStrategy
와 CashPaymentStrategy
두 클래스는 인터페이스 PaymentStrategy
를 implements 했기 때문에 PaymentStrategy
가 가지고 있는 pay() 메서드
, 추상 메서드,를 각 클래스에서 구체화해야 한다. 그리고 VendingMachine
클래스에서 PaymentStrategy
변수를 선언해주고 외부에서 클래스 주입이 가능하도록 setPaymentStrategy
함수를 구현한다. 그리고 최종적으로 VendingMachine
클래스에 pay() 메서드
를 구현한 후에 pay() 메서드가 호출되었을 때, paymentStrategy.pay()
메서드가 호출되게끔 구현한다.
이렇게 하면 pay() 메서드
가 호출되면 주입된 pay() 메서드
의 strategy에 따라 다른 pay() 메서드
가 호출된다.
const vendingMachine = new VendingMachine();
vendingMachine.setPaymentStrategy(new CashPaymentStrategy());
vendingMachine.pay(); // cash pay
vendingMachine.setPaymentStrategy(new CardPaymentStrategy());
vendingMachine.pay(); // card pay
위의 실행코드를 보면, vendingMachine
클래스를 생성하고 처음에 CashPaymentStrategy
클래스를 주입시킨다. 그리고 vendingMachine.pay()
메서드를 호출하게 되면, 'cash pay'가 콘솔에 찍히고 다시 CardPaymentStrategy
클래스를 주입시킨 후 아까와 같이 함수를 호출하면 'card pay'로 콘솔에 찍히는 것을 알 수 있다.
이렇게 전략패턴을 통해 메서드의 내부 로직을 수정할 필요 없이 전략을 바꿔 동적으로 행위의 수정을 할 수 있다.