템플릿 메서드 패턴

박찬우·2024년 2월 9일
0

스프링

목록 보기
33/88

템플릿 메서드 패턴

  • 정의
    • 부모 클래스에 알고리즘의 골격인 템플릿을 정의하고, 일부 변경되는 로직은 자식 클래스에 정의하는 것이다. 이렇게 하면 자식 클래스가 알고리즘의 전체 구조를 변경하지 않고, 특정 부분만 재정의할 수 있다. 결국 상속과 오버라이딩을 통한 다형성으로 문제를 해결하는 것이다.
  • 단점
    • 상속 관계이기 때문에 자식이 부모를 의존하게 된다
    • 부모 클래스의 코드가 바뀌면 자식 클래스도 그에 대한 영향을 받게된다
    • 단점 해결을 위해 전략 패턴을 사용한다
  • 이름 그대로 템플릿을 사용하는 방식
  • 템플릿은 기준이 되는 거대한 틀
  • 템플릿이라는 틀에 변하지 않는 부분을 몰아둔다. 그리고 일부 변하는 부분을 별도로 호출해서 해결
  • 추상클래스와 추상메소드를 이용하여 해결함
  • 동일한 패턴에 대하여 중복을 제거하고 수정 시에도 템플릿만 수정하면 되기 때문에 유지보수도 용이해짐

예)

  • 기존
    • 시간 관련 로직이 동일하게 사용됨
    @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);
    }
  • 변경
    • 시간 관련 로직을 템플릿 메소드 패턴을 적용
@Slf4j
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();
}
@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() {
        AbstractTemplate template1 = new SubClassLogic1();
        template1.execute();

        AbstractTemplate template2 = new SubClassLogic1();
        template2.execute();
    }

익명 내부 클래스 이용

  • 기존 코드는 클래스를 계속 만들어서 상속받아 사용해야하는 불편함이 있음
  • 이를 익명 내부 클래스를 이용하여 해결할 수 있다
    @Test
    void templateMethodV2() {

        AbstractTemplate template1 = new AbstractTemplate() {
            @Override
            protected void call() {
                log.info("비즈니스 로직1 실행");
            }
        };
        template1.execute();

        AbstractTemplate template2 = new AbstractTemplate() {
            @Override
            protected void call() {
                log.info("비즈니스 로직2 실행");
            }
        };
        template2.execute();
    }

제네릭 사용

  • 리턴값이 다를 수 있기 때문에 제네릭을 사용하여 해결
public abstract class AbstractTemplate<T> {

    private final LogTrace trace;

    public AbstractTemplate(LogTrace trace) {
        this.trace = trace;
    }

    public T excute(String message) {
        TraceStatus status = null;

        try {
            status = trace.begin(message);

            T result = call();

            trace.end(status);
            return result;
        } catch (Exception e) {
            trace.exception(status, e);
            throw e;
        }
    }

    protected abstract T call();
}
    public String request(String itemId) {

        AbstractTemplate<String> template = new AbstractTemplate<String>(trace) {
            @Override
            protected String call() {
                orderService.orderItem(itemId);
                return "ok";
            }
        };

        return template.excute("OrderController.request()");
    }
    
    public void orderItem(String itemId) {
        AbstractTemplate<Void> template = new AbstractTemplate<>(trace) {
            @Override
            protected Void call() {
                orderRepository.save(itemId);
                return null;
            }
        };

        template.excute("OrderService.orderItem()");
    }
profile
진짜 개발자가 되어보자

0개의 댓글