전략 패턴
전략 패턴은 실행 중에 알고리즘을 선택할 수 있게 하는 행위 디자인 패턴 입니다. 즉, 각각의 객체들이 할 수 있는 동작들일 미리 전략으로 만들어 놓고 동작을 동적으로 변경해야 한다면 해당 전략만 변경하여 객체의 동작이 바뀌도록 하는 패턴입니다.
구조
- 컨텍스트는 전략 구현체중 하나와 참조를 유지하고 전략 인터페이스를 통해서만 객체와 통신합니다.
- 전략 인터페이스는 모든 전략 구현체에 대한 공용 인터페이스입니다. 컨텍스트가 전략을 실행하는데 사용하는 메소드를 선언합니다.
- 인터페이스의 구현체는 컨텍스트가 사용하는 알고리즘의 다른 버전을 구현합니다.
- 컨텍스트는 알고리즘을 실행해야 할 때마다 해당 알고리즘과 연결된 전략 객체의 메소드를 호출합니다. 컨텍스트는 어떤 전략이 동작하는지, 혹은 어떤 알고리즘이 실행되는지 알 필요는 없습니다.
- 클라이언트는 특정 전략 객체를 만들어 컨텍스트에 전달합니다.
적용 가능한 경우
- 객체 내에서 서로 다른 알고리즘을 사용하고 런타임시 다른 알고리즘으로 전환하고자 하는 경우
- 일부 행위를 동작하는 방법만 다른 비슷한 클래스가 많은 경우
- 비즈니스 로직을 알고리즘의 세부 내용에서 분리하고자 하는 경우
장단점
장점
- 런타임에 객체 내부에서 사용되는 알고리즘을 바꿀수 있습니다.
- 알고리즘을 사용하는 코드에서 이를 구현하는 세부 정보를 분리할 수 있습니다.
- 상속을 구성으로 대체할 수 있습니다.
- 기존 컨텍스트를 변경하지 않고 새로운 전략을 도입함으로써 개방/폐쇄 원칙을 만족합니다.
단점
- 알고리즘이 많지 않고 자주 변경되지 않는다면 새로운 클래스와 인터페이스를 만들어 프로그램을 복잡하게 만들 이유가 없습니다.
- 개발자는 적절한 전략을 선택하기 위해 전략 간의 차이점을 알고 있어야 합니다.
- 최신 프로그래밍 언어에는 클래스 및 인터페이스를 추가하지 않아도 전략 객체를 사용하는 것과 동일한 효과를 낼 수 있는 방법이 있습니다.
예제
결제를 할수 있는 환경이 PC / Mobile 로 나뉘어져 있고 각각의 환경은 하나의 결제 방식을 선택할수 있으며 이를 바꿀수 있는 예제를 만들어 보겠습니다.
아래 코드는 결제 전략과 이를 구현한 세가지의 결제 방식(페이) 클래스 입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public interface PayStrategy {
void pay(int amount);
}
public class NaverPay implements PayStrategy{
@Override
public void pay(int amount) {
System.out.println("네이버 페이로 결제 - 금액 : " + amount);
}
}
public class KakaoPay implements PayStrategy{
@Override
public void pay(int amount) {
System.out.println("카카오 페이로 결제 - 금액 : " + amount);
}
}
public class TossPay implements PayStrategy{
@Override
public void pay(int amount) {
System.out.println("토스 페이로 결제 - 금액 : " + amount);
}
}
아래 코드는 결제 전략을 변수로 갖고 setter() 메소드를 통해 결제 전략을 주입할 수 있는 클래스 입니다. 해당 클래스를 통해 결제 환경을 만들 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class PayEnvironment {
private PayStrategy payStrategy;
public void setPayStrategy(PayStrategy payStrategy) {
this.payStrategy = payStrategy;
}
public void pay(int amount) {
if (payStrategy != null) {
payStrategy.pay(amount);
}
}
}
public class PC extends PayEnvironment {
}
public class Mobile extends PayEnvironment {
}
마지막 클라이언트 코드 입니다. 결제 환경에 따라 결제 전략을 선택할 수 있고 특정 조건을 만족하면 결제 전략을 변경합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static void main(String[] args) {
PayEnvironment pc = new PC();
PayEnvironment mobile = new Mobile();
pc.setPayStrategy(new KakaoPay());
mobile.setPayStrategy(new NaverPay());
pc.pay(10);
mobile.pay(30);
// 조건을 만족하면 결제 전략을 변경한다.
if (condition()) {
mobile.setPayStrategy(new TossPay());
}
mobile.pay(50);
}
public static boolean condition() {
return true;
}
결론
전략 패턴은 현업에서 알게 모르게 많이 사용하고 있는것 같습니다. 요즘 새로운 언어에서는 기본 기능으로도 간단하게 구현할 수 있으니 한번 찾아 보시길 추천 드리겠습니다.