이번 글에서는 디자인 패턴의 종류 즁 하나인 전략 패턴에 대해서 알아보자.
전략 패턴은 우리가 코드에서 은근히 많이 쓰는 패턴이다.
하지만 전략 패턴이 정확하게 무엇인지 알고 쓰는 경우가 많다. 이번 글을 통해서 전략 패턴에 대해서 자세하게 알아보고 적절하게 활용할 수 있도록 해보자.
객체나 클래스 사이의 알고리즘이나 책임 분배에 관련된 패턴이다.
한 객체가 수행할 수 없는 작업을 여러 개의 객체로 어떻게 분배하며 객체 사이의 결합도 최소화에 중점을 둔다.
패턴을 주로 클래스에 적용하는지 아니면 객체에 적용하는지에 따라 구분되는 패턴이다.
전략 패턴은 알고리즘을 특정 기준에 따라 분류해 정의하고, 각각을 캡슐화하여 사용시에 상호 교환이 가능하도록 만든다. 이 패턴을 사용하면 알고리즘을 사용하는 클라이언트와 독립적으로 알고리즘이 달라질 수 있다.
GoF 디자인 패턴에 나온 내용이다. 말만 들으면 어렵지만 이후 예시 코드를 보면 이해가 될 것이다. 아니면 미리 예시 코드를 보고 오는 것도 좋을 것 같다.
GoF에서 전략 패턴이 해결하는 문제는 다음과 같다.
한 클래스 안에 여러 알고리즘을 넣는 것 대신 런타임 시에 클래스의 설정을 바꾸어 다른 알고리즘을 실행 시킬 수 있다.
런타임 시에 알고리즘이 선택되고 교체될 수 있다.
즉, 런타임 시에 다형성을 이용해 클래스를 바꿔 해당 클래스가 가지고 있는 알고리즘을 사용할 수 있다는 장점이 있다.
public interface Strategy {
int algorithm();
}
public class Strategy1 implements Strategy {
@Override
public int algorithm() {
return 1;
}
}
public class Strategy2 implements Strategy {
@Override
public int algorithm() {
return 2;
}
}
위와 같이 Strategy 인터페이스가 있고 그를 상속 받는 Strategy1, Strategy2 클래스가 있다.
public class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public String operation() {
return "Context: Delegating an algorithm to a strategy: Result = "
+ strategy.algorithm();
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
}
Context라는 클래스는 Strategy를 DI 받는 클래스이다.
생성자 주입을 통해서 입력 받은 Strategy 인자를 통해 strategy가 바뀌게 된다.
public class MyApp {
public static void main(String[] args) {
// Creating a Context object
// and configuring it with a Strategy1 object.
Context context = new Context(new Strategy1());
// Calling an operation on context.
System.out.println("(1) " + context.operation());
// Changing context's strategy.
context.setStrategy(new Strategy2());
System.out.println("(2) " + context.operation());
}
}
Context를 생성할 때 Strategy1 객체를 넣으면 해당 인스턴스는 Strategy1을 DI 받게 된다.
그 후 Strategy2로 setStrategy를 하면 Strategy2가 DI 된다.
이를 통해 Strategy1의 알고리즘을 사용하고 싶으면 Strategy1을 주입하면 되고, Strategy2의 알고리즘을 사용하고 싶으면 Strategy2을 주입하면 된다.
전략 패턴을 이용하면 사용자가 선택한 사항에 따라서 DI를 바꿔줄 수 있게 된다.
예를 들어 사용자가 특정 쿠폰을 사용한다고 했을 때, 결제 금액이 달라지게 된다면 클라이언트 클래스의 DI를 해당 쿠폰 로직을 가지고 있는 클래스로 설정을 해서 간단하게 해결할 수 있다.