데코레이터 패턴
- 프록시를 사용하여 새로운 기능을 추가하는 것을 목적으로 하는 패턴
데코레이터 패턴 - 예제 코드1
- 데코레이터 패턴을 적용해보기 전에 일반적인 코드로 작성하면 어떻게 동작하는지 확인해보자.
Component
- 간단하게 operation 메소드만 추가한다.
package com.example.pureproxy.decorator;
public interface Component {
    String operation();
}RealComponent
- Component 인터페이스를 구현한다.
- operation에는 단순하게 데이터를 반환하는 로직을 작성한다.
DecoratorPatternClient
- Component 인터페이스에 의존하는 클라이언트 코드
package com.example.pureproxy.decorator;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class DecoratorPatternClient {
    private Component component;
    
    public DecoratorPatternClient(Component component) {
        this.component = component;
    }
    public void execute() {
        String result = component.operation();
        log.info("result={}", result);
    }
}DecoratorPatternTest
- 테스트를 생성한다.
package com.example.pureproxy.decorator;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@Slf4j
public class DecoratorPatternTest {
    @Test
    void noDecorator() {
        Component realComponent = new RealComponent();
        DecoratorPatternClient client = new DecoratorPatternClient(realComponent);
        client.execute();
    }
}- noDecorator 실행 로그com.example.pureproxy.decorator.RealComponent – RealComponent 실행 
 com.example.pureproxy.decorator.DecoratorPatternClient – result=data
데코레이터 패턴 - 예제 코드2
- 이번에는 실제로 데코레이터 패턴을 적용해보자.
- 응답 메시지를 꾸며주는 데코레이터를 생성하고 적용할 것이다.
- 데코레이터 적용 시 런타임 객체 의촌 관게는 아래와 같이 바뀐다.flowchart LR A[Client] ----> B[messageDecorator] B ----> C[reaalComponent]
MessageDecorator
- 응답 값을 꾸며주는 데코레이터 프록시
- Component 인터페이스를 구현한다.
- 프록시가 호출해야 하는 대상을 component에 저장한다.
package com.example.pureproxy.decorator;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class MessageDecorator implements Component {
    private Component component;
    public MessageDecorator(Component component) {
        this.component = component;
    }
    @Override
    public String operation() {
        log.info("MessageDecorator 실행");
        String result = component.operation();
        String decoResult = "*****" + result + "*****";
        log.info("MessageDecorator 꾸미기 적용 전={}, 적용 후={}", result, decoResult);
        return decoResult;
    }
}DecoratorPatternTest
- 데코레이터 적용 결과를 확인하기 위해 DecoratorPatternTest에 메서드를 추가하자.
- client → messageDecorator → realComponent 순서로 객체 의존 관계를 생성한다.
@Test
void decorator1() {
    Component realComponent = new RealComponent();
    Component messageDecorator = new MessageDecorator(realComponent);
    DecoratorPatternClient client = new DecoratorPatternClient(messageDecorator);
    client.execute();
}- decorator1 실행 로그com.example.pureproxy.decorator.MessageDecorator – MessageDecorator 실행 
 com.example.pureproxy.decorator.RealComponent – RealComponent 실행
 com.example.pureproxy.decorator.MessageDecorator – MessageDecorator 꾸미기 적용 전=data, 적용 후=**data**
 com.example.pureproxy.decorator.DecoratorPatternClient – result=**data**
- 실행 결과를 보면 MessageDecorator가 RealComponent를 호출하고 반환한 응답 메시지를 꾸며서 반환한 것을 확인할 수 있다.
데코레이터 패턴 - 예제 코드3
- 데코레이터는 하나만 적용할 수 있는게 아닌 복수로 적용할 수 있다.
- 이번에는 실행 시간을 측정하는 기능의 데코레이터를 생성하고 적용도 해보자.
- 데코레이터 적용 시 런타임 객체 의촌 관게는 아래와 같이 바뀐다.flowchart LR A[Client] ----> B[timeDecorator] B ----> C[messageDecorator] C ----> D[reaalComponent]
TimeDecorator
- 실행 기능을 측정하는 데코레이터다.
- 대상을 호출하기 전에 시간을 가지고 있다가, 대상의 호출이 끝나면 호출 시간을 로그로 남겨준다.
package com.example.pureproxy.decorator;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class TimeDecorator implements Component {
    private Component component;
    public TimeDecorator(Component component) {
        this.component = component;
    }
    @Override
    public String operation() {
        log.info("TimeDecorator 실행");
        long startTime = System.currentTimeMillis();
        String result = component.operation();
        long endTime = System.currentTimeMillis();
        long resultTime = endTime - startTime;
        log.info("TimeDecorator 종료 resultTime={}ms", resultTime);
        return result;
    }
}DecoratorPatternTest
- 데코레이터 적용 결과를 확인하기 위해 DecoratorPatternTest에 메서드를 추가하자.
- client → timeDecorator → messageDecorator → realComponent 순서로 객체 의존 관계를 생성한다.
@Test
void decorator2() {
    Component realComponent = new RealComponent();
    Component messageDecorator = new MessageDecorator(realComponent);
    Component timeDecorator = new TimeDecorator(messageDecorator);
    DecoratorPatternClient client = new DecoratorPatternClient(timeDecorator);
    client.execute();
}- decorator2 실행 로그com.example.pureproxy.decorator.TimeDecorator – TimeDecorator 실행 
 com.example.pureproxy.decorator.MessageDecorator – MessageDecorator 실행
 com.example.pureproxy.decorator.RealComponent – RealComponent 실행
 com.example.pureproxy.decorator.MessageDecorator – MessageDecorator 꾸미기 적용 전=data, 적용 후=**data**
 com.example.pureproxy.decorator.TimeDecorator – TimeDecorator 종료 resultTime=2ms
 com.example.pureproxy.decorator.DecoratorPatternClient – result=**data**
