Study/Design Pattern

템플릿 메소드 - 로그 추적기 적용 (1)

lsh2613 2023. 10. 2. 20:47

순수 자바로 로그 추적기를 개발하며 공부하던 중 최적화를 위해 템플릿 메소드를 적용해 보았다.

적용하기 전 템플릿 메소드에 대해 먼저 알아보자.

 

템플릿 메소드 패턴

작업에서 알고리즘의 골격을 정의하고 일부 단계를 하위 클래스로 연기함으로써 하위 클래스가 알고리즘의 구조를 변경하지 않고도 알고리즘의 특정 단계를 재정의할 수 있다. - GOF 디자인 패턴

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

 

 

적용 - 로그 추적기

템플릿 클래스

로그 추적기를 적용하기 위해 부모 클래스이자 추상 클래스이다.

각 레벨마다 로그를 추적해주는 Trace의 begin(), end()를 템플릿화하였고 자식 클래스에서 오버라이딩할(변경되는 로직) 메소드를 call()로 구현해주었다. 이제 각 자식클래스마다 필요로 하는 call() 메소드를 재정의하여 전체 흐름을 변경하지 않고 특정 부분 로직을 나눌 수 있게 된다.

public abstract class AbstractTemplate<T> {

    private final LogTrace trace;

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

    public T execute(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();
}

자식 클래스

Controller 레벨에서 익명함수로 상속받아 오버라이딩을 진행하였다. 이 부분에서는 ordeService의 orderItem을 호출하도록 재정의가 되었다.

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

    return template.execute("OrderController.request()");
}

 

이렇게 구현하여 실행하게 되면 로그 추적 메소드가 실행될 것이고 그 사이에 각 단계마다 오버라이딩한 call() 함수를 호출할 것이다.

단점

템플릿 메소드는 상속과 오버라이딩을 활용한 디자인 패턴이다. 그말은 즉슨 상속에서 오는 단점들을 그대로 가져온다.

특히, 자식 클래스 입장에서는 부모 클래스의 기능을 전혀 사용하지 않는다.

위 코드를 살펴보자. 익명함수로 상속받은 template은 call()함수롤 오버라이딩하여 execute를 호출할 뿐, 부모 클래스(AbstractTemplate)의 어떠한 메소드도 사용하지 않는다.

 

클래스가 부모 클래스와 컴파일 시점에 강하게 결합되는 문제가 발생한다. 상속을 받는다는 것은 특정 부모 클래스를 의존하는 것으로 부모 클래스의 기능을 사용하든 사용하지 않든 간에 부모 클래스를 강하게 의존한다. 

강하게 의존한다 : 자식 클래스의 코드에 부모 클래스의 코드가 명확하게 적혀있다

 

자식 클래스는 부모 클래스 기능을 전혀 사용하지 않는데, 부모 클래스를 알아야한다. 이런 잘못된 의존관계 때문에 부모 클래스를 수정하면, 자식 클래스에도 영향을 줄 수 있다. 이것은 좋은 설계가 아니다.

 

이를 해결하기 위해 전략 패턴을 도입할 수 있다.