이 전에 전략 패턴에 대해서 포스팅한 적이 있다.
전략 패턴은 실행 전에 먼저 조립을 해두고
실행을 한다. 그리고 중간에 필요한 특성을 바꿀 때는 setter를 사용해서 중간에 전략을 바꿀 수 있다. 하지만 싱글톤 방식에서 사용할 때는 동시성 문제가 발생할 가능성이 있다.
반대로, 실행 시점에 전략을 선택하는 방법이 있는데, 그것이 바로 템플릿 콜백 패턴
이다. 스프링 내부에서 이런 방식을 많이 사용하기 때문에, 스프링 안에서만 이렇게 부른다고 한다(RestTemplate, JdbcTemplate 등등).
콜백이란,
다른 코드의 인수로 넘겨주는 실행 가능한 코드를 말한다. 즉, 코드가 호출(call)이 되고, 뒤(back, after)에서 실행이 되는 것이다.
콜백이라는 의미가 이해하기 애매하지만, 파라미터로 넘어온 객체에게 로직 수행을 위임한다고 이해를 하면 될 것 같다.
그렇다면 콜백 패턴을 사용했을 경우와 아닌 경우를 비교하며 살펴보자.
조조 할인
,심야 할인
이다.public class Button {
private DiscountType discountType;
private static Button newInstance = new Button();
private Button(){}
public static Button getInstance(DiscountType discountType){
newInstance.discountType = discountType;
return newInstance;
}
public void click() {
System.out.println("할인이 적용됩니다.");
discountType.discount(PriceConst.MOVIE_PRICE);
System.out.println("결제를 시도해 주세요");
}
}
public class Main {
public static void main(String[] args) {
Button button1 = Button.getInstance(new MorningDiscountType()); //조조 할인
button1.click();
Button button2 = Button.getInstance(new NightDiscountType());/심야 할인
button2.click();
}
}
//결과
할인이 적용됩니다.
조조 할인입니다. 1000원 할인되어 11000원 입니다.
결제를 시도해 주세요
============
할인이 적용됩니다.
심야 할인이 적용됩니다. 2000원 할인되어 10000원 입니다.
결제를 시도해 주세요
setter 메서드
로 필드를 변경 시 동시성 문제가 발생할 가능성이 있다(스프링은 싱글톤을 사용).public class ButtonCallBack {
private static ButtonCallBack newInstance = new ButtonCallBack();
private ButtonCallBack(){}
public static ButtonCallBack getInstance(){
return newInstance;
}
public void click( DiscountType discountType) { //파라미터로 콜백 코드 전달.
System.out.println("할인이 적용됩니다.");
discountType.discount(PriceConst.MOVIE_PRICE);
System.out.println("결제를 시도해 주세요");
}
}
public class Main {
public static void main(String[] args) {
ButtonCallBack button = ButtonCallBack.getInstance();
button.click(new MorningDiscountType()); //실행 시점에 전략 설정
button.click(new NightDiscountType());
}
//결과
할인이 적용됩니다.
조조 할인입니다. 1000원 할인되어 11000원 입니다.
결제를 시도해 주세요
============
할인이 적용됩니다.
심야 할인이 적용됩니다. 2000원 할인되어 10000원 입니다.
결제를 시도해 주세요
할인이 적용됩니다.
...
결제를 시도해 주세요
이 부분이 할인 전략에도 변하지 않는 부분이다.
조조 할인입니다. 1000원 할인되어 11000원 입니다.
심야 할인이 적용됩니다. 2000원 할인되어 10000원 입니다.
즉, 변하지 않는 부분과 변하는 부분을 분리하고, 변하지 않는 부분을 템플릿으로서 공통으로 사용하는 것이다.
button.click(new MorningDiscountType());
button.click(new NightDiscountType());
....
public void click( DiscountType discountType) {
System.out.println("할인이 적용됩니다."); //변하지 않는 부분
discountType.discount(PriceConst.MOVIE_PRICE); //변하는 부분.
System.out.println("결제를 시도해 주세요");////변하지 않는 부분
}
public interface DiscountType {
public void discount(int originPrice);
}
...
public class Main {
public static void main(String[] args) {
ButtonCallBack button = ButtonCallBack.getInstance();
button.click(originPrice -> {
int totalPrice = 0;
totalPrice = originPrice - PriceConst.MORNING_DISCOUNT;
System.out.println("조조 할인입니다. " + PriceConst.MORNING_DISCOUNT +"원 할인되어 " + totalPrice +"원 입니다.");
});
}
}
//결과
할인이 적용됩니다.
조조 할인입니다. 1000원 할인되어 11000원 입니다.
결제를 시도해 주세요
전략 패턴과 템플릿 콜백 패턴은 각각 장단점이 존재하지만,
실행 시점에 유연하게 전략을 적용하라면 템플릿 콜백 패턴을 사용하는 것이 좋다.
Refernence