티스토리 뷰

템플릿 메서드 패턴

GOF에서는 템플릿 메서드 패턴을 "작업에서 알고리즘의 골격을 정의하고 일부 단계를 하위 클래스로 연기한다. 템플릿 메서드를 사용하면 하위 클래스가 알고리즘의 구조를 변경하지 않고도 알고리즘의 특정 단계를 재정의할 수 있다"고 말한다.


이 말은 부모 클래스에 알고리즘의 골격인 템플릿을 정의하고, 일부 변경되는 로직은 자식 클래스에 정의하는 것이다. 이렇게 하면 자식 클래스가 알고리즘의 전체 구조를 변경하지 않고, 특정 부분만 재정의할 수 있어 상속과 오버라이딩을 통한 다형성으로 문제를 해결하는 것이다.


이해를 돕기 위해 요리사들의 요리 시작부터 완성하기까지의 시간을 측정해야 한다고 가정해보고, 템플릿 메서드 패턴을 적용하여 구현해보자.


먼저 템플릿 메서드 패턴을 적용하기 전의 코드다.


템플릿 메서드 패턴 적용 전

  @Test
  void templateMethodV0() {
    startCookingKoreanChef();
    startCookingChineseChef();
  }

  private void startCookingKoreanChef() {
    long startTime = System.currentTimeMillis();

    // 비지니스 로직 실행
    log.info("한식 요리 시작 !");
    // 비지니스 로직 종료

    long endTime = System.currentTimeMillis();
    long resultTime = endTime - startTime;
    log.info("resultTime={}", resultTime);
  }

  private void startCookingChineseChef() {
    long startTime = System.currentTimeMillis();

    // 비지니스 로직 실행
    log.info("중식 요리 시작 !");
    // 비지니스 로직 종료

    long endTime = System.currentTimeMillis();
    long resultTime = endTime - startTime;
    log.info("resultTime={}", resultTime);
  }

결과


한식과 중식을 만드는 비지니스 로직에는 각각 시간을 측정하는 코드가 중복되어 들어있다. 만약 요리사가 100명이면 중복된 코드가 100개로 늘어날 것이다.


이제 템플릿 메서드 패턴을 적용해보자.



템플릿 메서드 패턴 적용 후

@Slf4j
public abstract class Chef {

  public void startCook() {
    long startTime = System.currentTimeMillis();

    // 비지니스 로직 실행
    call(); // 상속
    // 비지니스 로직 종료

    long endTime = System.currentTimeMillis();
    long resultTime = endTime - startTime;
    log.info("resultTime={}", resultTime);
  }

  protected abstract void call();

}

@Slf4j
public class KoreanChef extends Chef {

  @Override
  protected void call() {
    log.info("한식 요리 시작 !");
  }

}

@Slf4j
public class ChineseChef extends Chef {

  @Override
  protected void call() {
    log.info("중식 요리 시작 !");
  }

}

  /**
   * 템플릿 메서드 패턴 적용
   */
  @Test
  void templateMethodV1() {
    Chef koreanChef = new KoreanChef();
    koreanChef.startCook();

    Chef chineseChef = new ChineseChef();
    chineseChef.startCook();
  }

결과


결과는 템플릿 메서드 패턴 적용 전과 차이가 없다. 하지만 시간을 측정하는 중복된 코드를 Chef 클래스의 startCook()메서드에 선언하고 요리하는 로직은 추상 메서드 call()를 통해 하위클래스에서 구현하도록 하였다. 좋은 설계는 변경이 일어날 때 자연스레 드러난다. 이렇듯 중복되는 코드를 하나로 모듈화하고, 비지니스 로직을 분리함으로써, 추후 요리시간을 측정하는 로직(중복코드)이 변경되더라도 Chef클래스만 수정하면 된다.



템플릿 메서드 패턴은 상속을 사용하는데, 상속을 받는 다는 것은 특정 부모 클래스를 의존하고 있다는 것이다. 자식 클래스 입장에서는 부모 클래스의 기능을 전혀 사용하지 않는데, 부모 클래스를 알아야한다. 이것은 좋은 설계가 아니다. 그리고 이런 잘못된 의존관계 때문에 부모 클래스를 수정하면, 자식 클래스에도 영향을 줄 수 있다. 이에 대한 방안으로 전략 패턴을 이용한다면, 템플릿 메서드 패턴과 비슷한 역할을 하면서 상속의 단점을 제거할 수 있다.



Reference

김영한. 스프링 핵심 원리 - 고급편. 인프런.

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B3%A0%EA%B8%89%ED%8E%B8/dashboard

'Study' 카테고리의 다른 글

[Design Pattern] 템플릿 콜백 패턴  (0) 2022.05.09
[Design Pattern] 전략 패턴  (0) 2022.05.08
[Refactoring] 기본형 집착  (0) 2022.03.19
[Refactoring] 산탄총 수술  (0) 2022.03.18
[Refactoring] 뒤엉킨 변경  (0) 2022.02.27
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함