[Java] 템플릿 메서드 패턴 정리

Kai·2023년 12월 25일
0

디자인 패턴

목록 보기
2/5

☕ 개요


이번 글에서는 Java 뿐만 아니라 다른 언어들에서도 자주 사용되는 템플릿 메서드 패턴에 대해서 정리해보도록 하겠다.

바로 드가자


🧐 템플릿 메서드 패턴?


이름에서도 알 수 있듯이, 반복적으로 사용되는 어떠한 기능을 템플릿 형태로 만들어두고, 변하는 부분만을 Override하거나 파라미터로 받아서 처리하는 패턴을 의미한다.

굳이 비유하자면, 붕어빵을 만드는 과정에 비유할 수 있다.
모두 잘 알고 있듯이 팥붕이든 슈붕이든 모양은 동일하기 때문에 붕어빵을 찍어내는 주형틀에 슈크림이나 팥과 같은 내용물만 바꿔가면서 붕어빵을 만들곤 한다.
여기서 주형틀이 템플릿이고, 슈크림과 팥이 변하는 부분이라고 할 수 있다.

글보다는 코드로 보는 것이 아마 이해가 쉬울테니 바로 예시 코드를 알아보자.


💡 템플릿 메서드 패턴 예시


1. 가정

서로 다른 2개의 기능이 존재하고, 우리는 이 기능들에 대해서 소요되는 시간을 측정해야되는 상황에 처했다고 가정해보자. 그리고 나는 이 기능을 템플릿 메서드 패턴으로 구현해보도록 하겠다.

2. 템플릿 클래스 만들기

import lombok.extern.slf4j.Slf4j;

@Slf4j
public abstract class AbstractTemplate {

    public void execute() {
        long startMs = System.currentTimeMillis();
        run();
        long endMs = System.currentTimeMillis();
        log.info("SpendMs={}", endMs - startMs);
    }

    protected abstract void run();

}

이렇게 템플릿 역할을 할 클래스를 만들어주자.
이 클래스를 상속 받은 자식 클래스에서는 run()메서드만 오버라이드해주면 되는 것이다.

3. 구현체 만들기

위 클래스를 상속받은 2개의 클래스를 아래와 같이 만들어주자.

public class SomeClass1 extends AbstractTemplate {
    @Override
    protected void run() {
        try {
            Thread.sleep(1000);
            System.out.println("Hello world");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
public class SomeClass2 extends AbstractTemplate {
    @Override
    protected void run() {
        System.out.println("Bye world");
    }
}

첫 번째 클래스의 로직은 Sleep을 추가하였고, 다른 메세지를 출력하도록 하였다.

4. 테스트 코드로 확인해보기

import hello.springaop.trace.template.code.AbstractTemplate;
import hello.springaop.trace.template.code.SomeClass1;
import hello.springaop.trace.template.code.SomeClass2;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class TemplateMethodTest {

    @Test
    @DisplayName("templateMethod")
    public void templateMethod() throws Exception {
        SomeClass1 someClass1 = new SomeClass1();
        SomeClass2 someClass2 = new SomeClass2();
        AbstractTemplate someClass3 = new AbstractTemplate() { // 익명 클래스
            @Override
            protected void run() {
                System.out.println("Good morning");
            }
        };

        someClass1.execute();
        someClass2.execute();
        someClass3.execute();
    }
    
}

위 코드를 실행하면, 아래와 같이 로직을 실행하는 데 소요된 시간이 잘 출력이 되는 것을 확인할 수 있다.

간단한 예시 코드였지만 템플릿 메서드 패턴을 활용해서 성공적으로 중복을 제거할 수 있는 것을 확인하였다.


🤔 템플릿 메서드 패턴의 주의 사항


위 예시로 보았을 때, 꽤나 훌륭하게 중복을 제거한 것을 확인할 수 있었다.
하지만, 템플릿 메서드 패턴은 보다시피 상속을 통해서 구현이 되게 된다.
즉, 상속으로 인한 부모 클래스와 자식 클래스의 의존성이 아주 높아지는 것이다.
작은 기능이라면 크게 상관 없을 수도 없지만, 복잡하고 많은 양의 중복 로직을 템플릿 메서드 패턴을 통해서 해결한다고 생각해보면 약간 막막해지는 느낌이 없지 않아 있다. 😅

'객체 지향 언어에서 상속 쓰는 게 뭐 어때서?'라고 반문할 수 있을 것 같다.
이 것도 맞는 말이지만, 다른 패턴이나 기술들을 사용하면 의존성을 제거하고 코드의 중복을 깔끔하게 제거할 수도 있다.
그렇다고 해서 템플릿 메서드 패턴이 무조건 안좋다는 게 절대 아니고, 상황에 따라서 필요한 방식을 사용하면 될 것이다.

그리하여, 다음 글에서는 템플릿 메서드 패턴의 단점을 개선한 다른 패턴에 대해서도 알아보도록 하겠다.
그럼 이번 글은 이만 마치도록 하겠다. 🙏


🙏 참고


0개의 댓글