디자인 패턴/GOF

Template Method Pattern

kimjingyu 2023. 6. 10. 00:42
728x90

✏️ 개요

 템플릿 메서드 패턴은 부모 클래스에 변하지 않는 템플릿 코드를 둔다. 그리고 변하는 부분은 자식 클래스에 두고 상속과 오버라이딩을 사용해서 처리한다.

💻 자바에 적용

  • 템플릿
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();
}
  • 자식 클래스
public class SubClassLogic1 extends AbstractTemplate{
    @Override
    protected void call() {
        log.info("비즈니스 로직1 실행");
    }
}
  • 템플릿 메서드 패턴 적용
@Test
void templateMethodV1() {
    AbstractTemplate template1 = new SubClassLogic1();
    template1.execute();

    AbstractTemplate template2 = new SubClassLogic2();
    template2.execute();
}
  • 결과
00:33:25.838 [Test worker] INFO hello.advanced.trace.template.code.SubClassLogic1 -- 비즈니스 로직1 실행
00:33:25.840 [Test worker] INFO hello.advanced.trace.template.code.AbstractTemplate -- resultTime = 3
00:33:25.841 [Test worker] INFO hello.advanced.trace.template.code.SubClassLogic2 -- 비즈니스 로직2 실행
00:33:25.841 [Test worker] INFO hello.advanced.trace.template.code.AbstractTemplate -- resultTime = 0
  • 템플릿 메서드 패턴 인스턴스 호출 그림
    1. template.execute()를 호출하면 템플릿 로직인 AbstractTemplate.execute()를 실행한다.
    2. 중간에 call() 메서드를 호출하는데, 이 부분이 오버라이딩 되어있다.
    3. 따라서 현재 인스턴스인 SubClassLogic1 인스턴스의 SubClassLogic1.call() 메서드가 호출된다.

📌 익명 내부 클래스 사용

/**
 * 익명 내부 클래스 사용
 */
@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();
}
  • 결과
00:45:57.962 [Test worker] INFO hello.advanced.trace.template.TemplateMethodTest -- 클래스 이름1 = class hello.advanced.trace.template.TemplateMethodTest$1
00:45:57.964 [Test worker] INFO hello.advanced.trace.template.TemplateMethodTest -- 비즈니스 로직1 실행
00:45:57.964 [Test worker] INFO hello.advanced.trace.template.code.AbstractTemplate -- resultTime = 0
00:45:57.965 [Test worker] INFO hello.advanced.trace.template.TemplateMethodTest -- 클래스 이름2 = class hello.advanced.trace.template.TemplateMethodTest$2
00:45:57.965 [Test worker] INFO hello.advanced.trace.template.TemplateMethodTest -- 비즈니스 로직2 실행
00:45:57.965 [Test worker] INFO hello.advanced.trace.template.code.AbstractTemplate -- resultTime = 0

👉🏻 정리

 템플릿 메서드 패턴은 상속과 오버라이딩을 통한 다형성을 사용해서 변하는 부분과 변하지 않는 부분을 분리하는 방법이다.

🔨 문제점

템플릿 메서드 패턴은 상속을 사용한다. 따라서 상속에서 오는 단점들을 그대로 안고간다. 특히 자식 클래스가 부모 클래스와 컴파일 시점에 강하게 결합되는 문제가 있는데, 이 것이 의존관계에 대한 문제이다.

 

자식 클래스 클래스 입장에서는 부모 클래스의 기능을 전혀 사용하지 않는데도 불구하고 템플릿 메서드 패턴을 위해 부모 클래스를 상속받고 있다. 그리고 UML에서 상속을 받으면 삼각형 화살표가 자식 -> 부모를 향하고 있는데 이것이 의존관계를 반영하는 것이다.

 

추가로 템플릿 메서드 패턴은 상속 구조를 사용하기 때문에 별도의 클래스나 익명 내부 클래스를 만들어야 하는 부분도 복잡하다.

728x90