전략패턴은 특정한 계열의 알고리즘을 정의하고, 각 알고리즘을 캡슐화하여 실행 중에 알고리즘을 선택할 수 있게하는 디자인 패턴이다.
특정 매매 전략을 가지고 투자하는 투자자가 있다고 가정하자.
class Investor {
public void invest() {
System.out.println("전략 A 로 매매합니다.");
}
}
또 다른 전략 B가 추가되어 투자자에 따라 각기 다른 전략을 사용해야 한다고 한다.
이를 상속으로 풀어낸다면 다음과 같이 구현해볼 수 있다.
투자 전략에 대한 인터페이스를 만들고,
interface InvestStrategy {
invest();
}
각기 다른 투자전략을 구현한 뒤,
class InvestStrategyA implements InvestStrategy {
invest() {
System.out.println("전략 A 로 매매합니다.");
}
}
class InvestStrategyB implements InvestStrategy {
invest() {
System.out.println("전략 B 로 매매합니다.");
}
}
투자자에 따라 필요한 전략을 상속받아 사용하도록 구현한다.
class InvestorA extends InvestStrategyA {
//
}
class InvestorB extends InvestStrategyB {
//
}
이렇게 구현하는 경우, InvestorA 의 투자전략을 변경해야 하는 경우에는 기존 코드를 수정해야 한다. 이는 OCP 원칙을 위반한다.
또한 전략이 수정되어야 할 때마다 코드가 수정되어야 하므로 상황에 따른 실시간 전략 수정이 필요한 경우 이 설계로는 구현이 불가능하다.
더불어서 이는 리스코프 치환 원칙을 위반할 수 있는, 올바른 상속의 방식이 아니다
상속은 코드의 재사용이 아닌 타입계층의 구현을 위해서 사용되어야 한다.
전략패턴은 변하지 않는 부분을 추상화하고, 그에 대한 구현을 객체의 합성을 통해서 풀어낸다.
class Investor {
private InvestStrategy strategy;
// 특정 구현 클래스가 아닌, 추상화에 의존하게 함으로서 다형성을 얻을 수 있다.
public Investor(InvestStrategy strategy) {
this.strategy = strategy;
}
// invest 라는 개념을 추상화하고, 실제 구현을 strategy 객체에게 위임한다.
public void invest() {
strategy.invest();
}
public void changeStrategy(InvestStrategy strategy) {
this.strategy = strategy;
}
}
Investor investorA = new Investor(new InvestStrategyA());
Investor investorB = new Investor(new InvestStrategyB());
// 투자 방식이 추가되어도 별도의 투자자 클래스를 만들 필요가 없다.
// 실행 시점에 해당 전략을 주입받는 투자자 객체를 생성하면 된다.
investorA.invest() // 전략 A로 매매합니다.
investorB.invest() // 전략 B로 매매합니다.
// 실행중에 투자전략 변경이 가능하다.
investorA.changeStrategy(new InvestStrategyB());
investorA.invest() // 전략 B로 매매합니다.