개요
템플릿 메서드 패턴은 알고리즘의 구조를 정의하면서도 하위 클래스에서 특정 단계를 재정의할 수 있도록 해주는 디자인 패턴입니다. 이 패턴은 반복되는 처리 흐름이 존재하지만, 각 단계의 구체적인 구현은 다를 때 유용합니다.
사용 환경
| 소프트웨어 | 버전 |
|---|---|
| JDK | 1.8 |
패턴 정의
알고리즘의 전반적인 틀을 추상 클래스에서 정의하고, 일부 단계는 하위 클래스에서 구현하도록 위임합니다. 이를 통해 알고리즘의 구조는 유지되면서도 핵심 로직은 확장 가능하게 됩니다.
설명
예를 들어, 어떤 시스템이 공통된 실행 흐름(초기화 → 시작 → 처리 → 종료)을 가지지만, 각 단계의 구체적인 동작은 시스템에 따라 달라질 때, 템플릿 메서드 패턴을 사용하면 전체 흐름을 일관성 있게 관리하면서도 유연한 확장이 가능합니다. 이때 부모 클래스는 전체 흐름을 고정하고, 하위 클래스는 자신에게 맞는 구현만 제공하면 됩니다.
실제 예시: 데이터 처리 프로세스
요구사항
다양한 유형의 데이터(메시지, 책 정보 등)를 처리하는 공통 백엔드 시스템을 개발해야 합니다. 모든 처리 과정은 동일한 순서를 따르되, 각각의 구체적 처리 방식은 다르게 적용되어야 합니다.
설계 아이디어
- 공통 흐름: 초기화 → 시작 → 처리 → 종료
- 하위 클래스가 각 단계를 구체화
- 전체 흐름은 변경 불가 (final 메서드로 제한)
- 선택적 초기화 여부는 외부 설정 가능 (하위 클래스에서 조건 제어)
코드 예시
기본 처리 템플릿 클래스
@Slf4j
public abstract class DataProcessor {
protected boolean requiresInitialization = true;
protected abstract void initialize();
protected abstract void startProcessing();
protected abstract boolean execute();
protected abstract void cleanup();
public final void run() {
if (requiresInitialization) {
log.info("→ 초기화 수행");
initialize();
} else {
log.info("→ 초기화 생략");
}
log.info("==================================");
log.info("→ 처리 시작");
startProcessing();
log.info("==================================");
log.info("→ 작업 실행");
boolean success = execute();
if (success) {
log.info("✅ 작업 성공");
} else {
log.error("❌ 작업 실패");
}
log.info("==================================");
log.info("→ 정리 및 종료");
cleanup();
}
}
메시지 처리 클래스
@Slf4j
public class MessageProcessor extends DataProcessor {
public MessageProcessor() {
this.requiresInitialization = true;
}
@Override
protected void initialize() {
log.info("🔧 메시지 수신 스레드 초기화");
}
@Override
protected void startProcessing() {
log.info("▶️ 메시지 수신 시작");
}
@Override
protected boolean execute() {
log.info("🔄 메시지 정제 중 (1초 소요)");
return true;
}
@Override
protected void cleanup() {
log.info("⏹️ 메시지 수신 스레드 종료");
}
}
도서 정보 처리 클래스
@Slf4j
public class BookProcessor extends DataProcessor {
public BookProcessor() {
this.requiresInitialization = false;
}
@Override
protected void initialize() {
log.info("⚠️ 도서 처리에는 초기화 필요 없음");
}
@Override
protected void startProcessing() {
log.info("📚 도서 파일 읽기 스레드 시작");
}
@Override
protected boolean execute() {
log.info("📖 도서 데이터 읽는 중...");
log.info("📖 읽기 완료");
return true;
}
@Override
protected void cleanup() {
log.info("🔚 도서 스레드 종료");
}
}
클라이언트 코드
@Slf4j
public class Application {
public static void main(String[] args) {
DataProcessor bookHandler = new BookProcessor();
bookHandler.run();
log.info("----------------------------------------");
DataProcessor msgHandler = new MessageProcessor();
msgHandler.run();
}
}
출력 결과
22:59:53.156 [main] INFO DataProcessor - → 초기화 생략
22:59:53.170 [main] INFO DataProcessor - ==================================
22:59:53.170 [main] INFO DataProcessor - → 처리 시작
22:59:53.170 [main] INFO BookProcessor - 📚 도서 파일 읽기 스레드 시작
22:59:53.170 [main] INFO DataProcessor - ==================================
22:59:53.170 [main] INFO DataProcessor - → 작업 실행
22:59:53.170 [main] INFO BookProcessor - 📖 도서 데이터 읽는 중...
22:59:53.170 [main] INFO BookProcessor - 📖 읽기 완료
22:59:53.172 [main] INFO DataProcessor - ✅ 작업 성공
22:59:53.172 [main] INFO DataProcessor - ==================================
22:59:53.172 [main] INFO DataProcessor - → 정리 및 종료
22:59:53.172 [main] INFO BookProcessor - 🔚 도서 스레드 종료
22:59:53.172 [main] INFO Application - ----------------------------------------
22:59:53.172 [main] INFO DataProcessor - → 초기화 수행
22:59:53.172 [main] INFO MessageProcessor - 🔧 메시지 수신 스레드 초기화
22:59:53.172 [main] INFO DataProcessor - ==================================
22:59:53.172 [main] INFO DataProcessor - → 처리 시작
22:59:53.172 [main] INFO MessageProcessor - ▶️ 메시지 수신 시작
22:59:53.172 [main] INFO DataProcessor - ==================================
22:59:53.172 [main] INFO DataProcessor - → 작업 실행
22:59:53.172 [main] INFO MessageProcessor - 🔄 메시지 정제 중 (1초 소요)
22:59:53.172 [main] INFO DataProcessor - ✅ 작업 성공
22:59:53.173 [main] INFO DataProcessor - ==================================
22:59:53.173 [main] INFO DataProcessor - → 정리 및 종료
22:59:53.173 [main] INFO MessageProcessor - ⏹️ 메시지 수신 스레드 종료
장점
- 공통 로직은 한 번 정의하여 중복 제거
- 처리 흐름의 일관성 보장
- 확장성과 유지보수성 향상
단점
- 하위 클래스의 오류가 상위 흐름에 영향을 줄 수 있음
- 하위 클래스가 부모의 흐름을 의도적으로 수정할 경우 의도와 다른 동작 발생 가능성
결론
템플릿 메서드 패턴은 반복적인 처리 흐름이 존재할 때 매우 유용하며, 코드의 재사용성과 구조적 일관성을 높이는 데 기여합니다. 다만, 실제 적용 시에는 하위 클래스의 책임과 부담을 고려해 적절한 설계가 필요합니다.