템플릿 메서드 패턴이란?
@Test
void templateMethodV0() {
logic1();
logic2();
}
private void logic1() {
long startTime = System.currentTimeMillis();
// 비즈니스 로직 실행
log.info("비즈니스 로직1 실행");
// 비즈니스 로직 종료
long endTime = System.currentTimeMillis();
long resultTime = endTime - startTime;
log.info("resultTime = {}", resultTime);
}
private void logic2() {
long startTime = System.currentTimeMillis();
// 비즈니스 로직 실행
log.info("비즈니스 로직2 실행");
// 비즈니스 로직 종료
long endTime = System.currentTimeMillis();
long resultTime = endTime - startTime;
log.info("resultTime = {}", resultTime);
}
위와 같이 각각의 비즈니스 로직을 가지고 있는 logic1, logic2 메서드가 있다고 하자.
두메서드에는 동일하게 메서드의 실행시간을 출력하는 로직이 포함되어 있다.
만약 모든 메서드에 이런 식으로 실행시간을 측정하는 로직을 추가한다면 코드의 중복이 늘어나게 된다.
또한 만약 공통된 로직을 수정해야 한다면 모든 메서드를 수정해야 하기에 유지보수에도 좋지 않다.
이럴 때 변하지 않는 로직(공통된 부분)과 변하는 로직(비즈니스 로직)을 분리하는 디자인 패턴을 템플릿 메서드 패턴이라고 한다.

템플릿 메서드 패턴을 간단히 도식으로 나타내면 위와 같다.
템플릿 메서드 패턴은 이름 그대로 템플릿을 사용하는 방식이다. 템플릿에는 변하지 않는 부분을 몰아둔다.
추상 클래스 (공통된 로직을 포함)
@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();
}
logic1, logic2의 변하지않는 부분(공통된 로직)을 추상클래스에 정의하고, 변하는 부분(비즈니스 로직)을 추상메서드로 정의한다.
자식 클래스(추상 클래스를 구현한 클래스)
@Slf4j
public class SubClassLogic1 extends AbstractTemplate {
@Override
protected void call() {
log.info("비즈니스 로직1 실행");
}
}
이제 해당 추상클래스를 상속받는 자식 클래스를 생성한다. 그리고 추상메서드인 call을 구현해준다.
@Test
void templateMethodV1() {
AbstractTemplate template1 = new SubClassLogic1();
template1.execute();
AbstractTemplate template2 = new SubClassLogic2();
template2.execute();
}
추상 클래스를 구현한 자식 클래스를 통해 이전과 동일하게 실행시간을 로그로 찍는 코드를 작성할 수 있다. 하지만 이전과는
다르게 코드의 중복을 없앨 수 있다. 이와 같이 추상클래스를 구현하는 클래스를 따로 작성해도 되지만 익명 내부 클래스를 사용할
수도 있다.
익명 내부 클래스 사용
@Test
void templateMethodV2() {
AbstractTemplate template1 = new AbstractTemplate() {
@Override
protected void call() {
log.info("비즈니스 로직1 실행");
}
};
log.info("클래스 이름1 = {}", template1.getClass());
template1.execute();
AbstractTemplate template2 = new AbstractTemplate() {
@Override
protected void call() {
log.info("비즈니스 로직2 실행");
}
};
log.info("클래스 이름2 = {}", template2.getClass());
template2.execute();
}
익명 내부 클래스를 사용하면 따로 구현클래스를 작성할 필요 없이 선언과 동시에 추상메서드를 오버라이딩 할 수 있다.
따라서 따로 구현 클래스를 작성할 필요가 없어진다.


익명 클래스의 이름을 확인해 보면 해당 클래스의 이름에 $1, $2, ... 와 같이 이름이 붙는 것을 확인할 수 있는데,
이는 익명 내부 클래스의 경우 이름이 없기 때문에, 컴파일러가 이름을 자동으로 붙이기 때문이다.

추상 클래스와 구현 클래스에서의 호출관계를 살펴보면 위와 같다.
추상 클래스의 execute 메서드를 실행하다 중간에 call 메서드를 호출하는데
이때 call 메서드가 자식 클래스에서 오버라이딩 되어 있기 때문에 자식 클래스에서
재정의된 call메서드가 실행된다. 따라서 템플릿 메서드 패턴을 사용할 때 공통된 로직은
구현하지 않고 변경되는 로직만 오버라이딩하여 구현할 수 있었다.
템플릿 메서드 패턴의 문제점

템플릿 메서드 패턴은 상속을 사용한다. 따라서 상속에서 발생하는 문제점들이 동일하게 발생한다.
상속을 받는다는 것은 부모 클래스를 의존하고 있다는 것이고, 그 말은 부모 클래스의 기능을 사용하든,
사용하지 않든 자식 클래스는 부모 클래스를 알아야 한다는 것이다. 따라서 부모 클래스를 수정하면
자식 클래스에도 영향을 줄 수 있다. 추가로 별도의 클래스나 익명 클래스를 만들어야 하는 부분도 복잡하다.
이런 것들을 깔끔하게 개선하기 위해서는 전략 패턴(Strategy Patterrn)을 사용하면 된다.
'Back-end' 카테고리의 다른 글
테스트와 TDD(Test Driven Development) (0) | 2025.03.31 |
---|---|
데이터베이스를 최적화하는 방법들 (0) | 2025.03.01 |
Redis 알아보기 (0) | 2025.02.27 |