템플릿 메서드 패턴
템플릿 메서드 패턴이란?
- 핵심기능: 해당 객체가 제공하는 고유기능
- 메서드가 제공하는 비즈니스 로직
- 부가기능: 핵심기능을 보조하기 위한 기능
- 로깅, 트랜잭션 등 핵심기능을 보조하며 핵심기능과 함께 사용되는 기능
- 여러 핵심기능에 대해 동일한 부가기능을 제공
- 부가기능만 단독으로 사용는 경우는 않음
- 핵심 기능
public void orderItem(String itemId) {
orderRepository.save(itemId);
}
- 부가 기능(로깅) 추가
public void orderItem(String itemId) {
TraceStatus status = null;
try {
status = trace.begin("OrderService.orderItem()");
orderRepository.save(itemId); // 핵심 기능
trace.end(status)
} catch (Exception e) {
trace.exception(status, e);
throw e;
}
}
- 핵심기능이 많을수록 부가기능에 대한 코드 사용처도 늘어남
- 중간에 핵심 기능의 코드만 다르고 부가 기능의 코드는 동일함
- 즉 부가 기능의 코드는 중복코드
- 핵심 기능만 작성하고 부가 기능 코드는 재사용하도록 작성할 필요가 있음
- 좋은 설계는 변하는 것과 변하지 않는 것을 분리하는 설계
- 핵심 기능과 부가 기능을 분리해서 모듈화
- 이를 해결할수 있는 디자인 패턴이 템플릿 메서드 패턴(Template Method Pattern)
템플릿 메서드 예시
추상 클래스를 사용한 예시
public class AbstractTemplate {
public void execute() {
TraceStatus status = null;
try {
status = trace.begin("OrderService.orderItem()");
call();
trace.end(status)
} catch (Exception e) {
trace.exception(status, e);
throw e;
}
}
protected abstract void call();
}
public class SubClass extends AbstractTemplate {
@Override
protected void call() {
orderRepository.save(itemId);
}
}
- 핵심은 상속기반의 다형성
- 호출하는 클라이언트는
AbstractTemplate
인스턴스의 call 메서드 호출
- 호출하는 클라이언트는
- 부가 기능에 수정이 필요하면
AbstractTemplate
만 수정하면 됨- 단일 책임 원칙이 잘 지켜진 것
익명 내부 클래스를 사용한 예시
- 상속 기반의 템플릿 메서드 패턴은 매번 클래스를 정의해야함
- 익면 내부 클래스를 사용하면 이런 단점을 보완할 수 있음
- 인스턴스를 생성하면서 동시에 자식 클래스를 정의가능
public class Service {
void templateMothod() {
AbstractTemplate template1 = new AbstractTemplate() {
@Override
protected void call() {
orderRepository.save(itemId);
}
}
template1.execute();
}
}
템플릿 메서드 적용
public abstract class AbstractTemplate<T> {
private final LogTrace trace;
public AbstractTemplate(LogTrace trace) {
this.trace = trace;
}
public T execute(String message) {
TraceStatus status = null;
try {
status = trace.begin(message);
T result = call();
trace.end(status)
return result;
} catch (Exception e) {
trace.exception(status, e);
throw e;
}
}
protected abstract T call();
}
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()");
}
}
}
좋은 설계란?
- 진정한 좋은 설계란 변경이 일어날 때 자연스럽게 드러남
- 단일 책임 원칙(SRP)
템플릿 메서드 패턴 - 정의
- 작업에서 알고리즘의 골격을 정의하고 일부 단계를 하위 클래스로 연기
- 하위 클래스가 알고리즘의 구조를 변경하지 않고도 알고리즘의 특정 단계를 재정의 가능
- 상속과 오버라이딩을 통한 다형성으로 문제를 해결
- 그러나 자식클래스가 부모클래스를 의존함, 특히 컴파일 시점에 강하게 결합됨
- 자식 클래스는 부모 클래스를 사용하지 않지만 상속을 사용하기 때문
- 템플릿 메서드 패턴과 비슷하면서 상속의 단점을 제거하는 디자인 패턴이 전략 패턴