728x90
좋은 설계는 변하는 것과 변하지 않는 것을 분리하는 것이다. 예를들어, 핵심 기능 부분이 변하고, 부가 기능 부분이 변하지 않는 부분이라면, 이 둘을 분리해서 모듈화해야 한다.
템플릿 메서드 패턴은 이런 문제를 해결하는 디자인 패턴이다.
다음에서 추상 클래스를 활용해서 부모 클래스에 변하지 않는 템플릿 코드를 두고, 변하는 부분은 자식 클래스에 두고 상속과 오버라이딩을 사용해서 처리하는 것을 볼 수 있다.
/**
* 변하지 않는 부분
*/
@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 실행");
}
}
@Slf4j
public class SubClassLogic2 extends AbstractTemplate{
@Override
protected void call() {
log.info("비즈니스 로직2 실행");
}
}
이렇게 템플릿 메서드 패턴은 다형성을 이용해서 변하는 부분과 변하지 않는 부분을 분리하는 방법이다.
또한 아래와 같이 익명 내부 클래스를 사용하여 객체 인스턴스를 생성하면서 동시에 생성할 클래스를 상속 받은 자식 클래스를 정의할 수 있다.
@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();
}
실행 결과에서 자바가 임의로 만들어주는 익명 내부 클래스 이름이 TemplateMethodTest$1 처럼 생성되는 것을 확인할 수 있다.
00:48:10.427 [Test worker] INFO hello.advanced.trace.template.TemplateMethodTest -- 클래스 이름1 = class hello.advanced.trace.template.TemplateMethodTest$1
00:48:10.430 [Test worker] INFO hello.advanced.trace.template.TemplateMethodTest -- 비즈니스 로직1 실행
00:48:10.430 [Test worker] INFO hello.advanced.trace.template.code.AbstractTemplate -- resultTime = 0
00:48:10.430 [Test worker] INFO hello.advanced.trace.template.TemplateMethodTest -- 클래스 이름2 = class hello.advanced.trace.template.TemplateMethodTest$2
00:48:10.430 [Test worker] INFO hello.advanced.trace.template.TemplateMethodTest -- 비즈니스 로직2 실행
00:48:10.430 [Test worker] INFO hello.advanced.trace.template.code.AbstractTemplate -- resultTime = 0
Template Method Pattern 적용
템플릿 메서드 패턴을 따르면 변하는 코드와 변하지 않는 코드를 명확히 분리할 수 있다. 로그를 출력하는 템플릿 역할을 하는 코드는 모두 AbstractTemplate에 담아두고, 변하는 코드는 자식 클래스를 만들어서 분리한다.
그 결과 아래 템플릿 메서드 패턴을 적용한 코드는 핵심 기능과 템플릿을 호출하는 코드가 섞여 있게 된다. 따라서 핵심 기능 구현에 조금 더 집중할 수 있게 된다.
@Service
@RequiredArgsConstructor
public class OrderService {
private final OrderRepository orderRepository;
private final LogTrace trace;
public void orderItem(String itemId) {
AbstractTemplate<Void> template = new AbstractTemplate<>(trace) {
@Override
protected Void call() {
orderRepository.save(itemId);
return null;
}
};
template.execute("OrderService.orderItem()");
}
}
728x90
'디자인 패턴 > GOF' 카테고리의 다른 글
Proxy Pattern, Decorator Pattern (0) | 2023.09.22 |
---|---|
Strategy Pattern (0) | 2023.06.10 |
Template Method Pattern (0) | 2023.06.10 |