오봉이와 함께하는 개발 블로그
스프링 핵심 원리 - 고급편 > 템플릿 메서드 패턴 (시작, 예제 1, 예제 2) 본문
728x90
템플릿 메서드 패턴 - 시작
- 로그 추적기 도입 전 V0 코드
@GetMapping("/v0/request") public String request(@RequestParam("itemId") String itemId) { orderService.orderItem(itemId); return "ok"; } public void orderItem(String itemId) { orderRepository.save(itemId); }
- 로그 추적기 도입 후 V3 코드
@GetMapping("/v3/request")
public String request(@RequestParam("itemId") String itemId) {
TraceStatus status = trace.begin("OrderController.request()");
try {
orderService.orderItem(itemId);
trace.end(status);
return "ok";
} catch (Exception e) {
trace.exception(status, e);
throw e; // 꼭 예외를 다시 던져야 함
}
}
public void orderItem(String itemId) {
TraceStatus status = trace.begin("OrderService.orderItem()");
try {
orderRepository.save(itemId);
trace.end(status);
} catch (Exception e) {
trace.exception(status, e);
throw e;
}
}
로그 추적기 추가로 인하여 코드 가독성이 떨어지게 되었다.
핵심 기능 / 부가 기능
- 핵심 기능은 해당 객체가 제공하는 고유 기능
- 예를 들어서
orderService
의 핵심 기능은 주문 로직
- 예를 들어서
- 부가 기능은 핵심 기능을 보조하기 위해 제공되는 기능
- 예를 들어 로그 추적 로직, 트랜잭션 등
V0는 핵심 기능만 있지만, V3 코드는 핵심 기능과 부가 기능이 함께 섞여 있어 핵심 기능 코드보다 부가 기능 코드가 더 많은 상황이 되었다.
복잡한 비즈니스 코드에 갑자기 로그 추적기를 도입하게 되었을 때, 혹은 이미 도입된 로그 추적기를 수정하게 된다면 여러 클래스에 얽힌 로그 추적기의 모든 코드를 변경 해야할 수도 있는 상황이 된다.
변하는 것과 변하지 않는 것을 분리
좋은 설계는 변하는 것과 변하지 않는 것을 분리하는 것이다.
위 코드들에서는 핵심 기능은 변하고, 로그 추적기는 변하지 않는 부분이다.
이 둘을 분리하여 모듈화해야 좋은 설계가 될 수 있다.
템플릿 메서드 패턴 - 예제 1
- 예제 코드
@Slf4j public class TemplateMethodTest { @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()
는 시간을 측정하는 부분과 비즈니스 로직을 실행하는 부분이 함께 존재한다.
변하는 부분: 비즈니스 로직
변하지 않는 부분: 시간 측정
이제 템플릿 메서드 패턴을 사용해서 변하는 부분과 변하지 않는 부분을 분리하자.
템플릿 메서드 패턴 - 예제 2
템플릿 메서드 패턴 구조 그림
- AbsctractTemplate.java
@Slf4j public abstract class AbstractTemplate { public void execute() { long startTime = System.currentTimeMillis(); // business logic start call(); // business logic end long endTime = System.currentTimeMillis(); long resultTime = endTime - startTime; log.info("resultTime = {}", resultTime); } protected abstract void call(); }
- 템플릿 메서드 패턴
- 이름 그대로 템플릿을 사용하는 방식
- 템플릿에 변하지 않는 부분을 몰아두고, 일부 변하는 부분을 별도로 호출하여 해결한다.
- 위 코드에서 시간 측정 로직은 변하지 않는 부분이고, 변하는 부분은
call()
이다.
- SubClassLogic1.java & SubClassLogic2.java
@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 templateMethodV1() {
SubClassLogic1 template1 = new SubClassLogic1();
template1.execute();
SubClassLogic2 template2 = new SubClassLogic2();
template2.execute();
}
템플릿 메서드 패턴 인스턴스 호출 그림
template1.execute()
를 호출하면 템플릿 로직인 AbstractTemplate.execute()
를 실행
여기서 중간에 call()
메서드를 호출하는데, 이 부분이 오버라이딩 되어있다.
따라서 현재 인스턴스인 SubClassLogic1
인스턴스의 SubClassLogic1.call()
메서드가 호출
이로 인해 단일 책임 원칙
을 지킬 수 있게 되었다.
출처: 김영한 지식공유자님의 스프링 핵심 원리 고급편
728x90
'BE > Spring' 카테고리의 다른 글
스프링 핵심 원리 - 고급편 > 전략 패턴 (시작, 예제 1) (0) | 2024.05.10 |
---|---|
스프링 핵심 원리 - 고급편 > 템플릿 메서드 패턴 (적용 1, 적용 2, 정의) (0) | 2024.05.10 |
Spring Framework - 테스트 코드 작성 (기본 - mock, spy, MockBean, SpyBean, MockWebServer) (0) | 2024.03.31 |
스프링 핵심 원리 - 고급편_쓰레드 로컬(주의사항) (0) | 2024.03.17 |
스프링 핵심 원리 - 고급편_쓰레드 로컬(쓰레드 로컬 동기화 개발, 적용) (1) | 2024.03.17 |
Comments