작업에서 알고리즘의 골격을 정의하고 일부 단계를 하위 클래스로 연기하자.
템플릿 메서드를 사용하면 하위 클래스가 알고리즘의 구조를 변경하지 않고도 알고리즘의 특정 단계를 재정의할 수 있다.
로직의 핵심 기능과 부가 기능(로그, 트랜잭션 등)을 모듈화해서 분리하는 방법이다.
여러개의 클래스에 로그를 남겨야 한다고 가정할 때
각 비지니스 로직에 로그를 남기는 부가 기능을 작성하는 건 비효율적이고 코드가 지저분해진다.
이러한 경우에 변하는 부분과 변하지 않는 부분을 모듈화해서 분리해내 효율적으로 처리할 수 있다.
클래스에 변하지 않는 것(부가 기능)을 execute() 메소드에 몰아넣는다.
이러면 이것이 템플릿이 되고 변하는 부분(핵심 기능)은 call() 메서드를 호출해서 처리한다.
변하지 않는 것은 부모 클래스로 두고, 자식 클래스에 변하는 부분을 둬 상속과 오버라이딩을 사용해서 처리한다.
//부모 클래스
@Slf4j
public abstract class AbstractTemplate {
public void execute(){
long startTime = System.currentTimeMillis();
//비지니스 로직 실행
call();
//비지니스 로직 종료
long endTime = System.currentTimeMillis();
long resultTime = endTime - startTime;
log.info("resultTime={}", resultTime);
}
protected abstract void call();
}
//자식 클래스
@Slf4j
public class SubClassLogic1 extends AbstractTemplate{
@Override
protected void call() {
//비지니스 로직 실행
log.info("비지니스 로직1 실행");
//비지니스 로직 종료
}
}
//테스트 메서드
@Test
void templateMethodV1(){
AbstractTemplate template1 = new SubClassLogic1();
AbstractTemplate template2 = new AbstractTemplate() {
@Override
protected void call() {
//비지니스 로직 실행
log.info("비지니스 로직2 실행");
//비지니스 로직 종료
}
};
template1.execute();
template2.execute();
}
// 결과
SubClassLogic1 - 비지니스 로직1 실행
AbstractTemplate - resultTime=6
SubClassLogic2 - 비지니스 로직2 실행
AbstractTemplate - resultTime=1
상속에서 오는 단점을 그대로 안고간다.
자식 클래스가 부모 클래스와 컴파일 시점에 강하게 결합되는 문제가 있다.
자식 클래스 입장에서는 부모 클래스의 기능을 전혀 사용하지 않지만 이 디자인 패턴을 위해
자식 클래스는 부모 클래스를 상속 받고 있다. 이는 부모 클래스 기능을 사용하든 안 하든 강하게 의존한다는 의미다.