-
데코레이터 패턴스프링/스프링 AOP 2023. 9. 10. 16:53
데코레이터 패턴은 프록시를 사용하는 디자인 패턴 중에서 부가 기능을 추가시켜주는 패턴이다.
Component 인터페이스
public interface Component { String operation(); }
Component의 구현체인 RealCcomponent
@Slf4j public class RealComponent implements Component { @Override public String operation() { log.info("RealComponent 실행"); return "data"; } }
client 계층 - DecoratorPatternClient
@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); } }
data를 실행시켜주는 로직이다. 이렇게만 구현해놓으면 정상적으로 실행이 될 것이다. 이제 여기다가 부가기능을 추가할 것이다.
위의 사진처럼 구현을 할 것인데 프록시를 체인 형태로 만들어서 로직을 수행하는데 걸리는 시간과 message를 꾸며주는 부가기능을 넣을 것이다.
첫 번째 프록시 - TimeDecorator
@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; } }
로직 수행 시간을 알려주는 TimeDecorator이다. operation() 메서드에서 component의 operation()을 호출하면서 로직 수행 시 걸리는 시간을 계산해서 로그로 출력해준다.
두 번째 프록시 - MessageDecorator
@Slf4j public class MessageDecorator implements Component { private Component component; public MessageDecorator(Component component) { this.component = component; } @Override public String operation() { log.info("MessageDecorator 실행"); // data -> *****data***** String result = component.operation(); String decoResult = "*****" + result + "*****"; log.info("MessageDecorator 꾸미기 적용 전={}, 적용 후={}", result, decoResult); return decoResult; } }
두 번째로 반환되는 데이터의 앞 뒤에 별을 다섯개 씩 붙여주는 로직이다. 별로 의미는 없지만, 부가적인 기능을 프록시로 넣을 수 있고, 체인으로 여러 프록시를 결합할 수 있다는 것을 확인하기 위해 추가했다.
테스트 코드
@Test void decorator() { Component realComponent = new RealComponent(); Component messageDecorator = new MessageDecorator(realComponent); Component timeDecorator = new TimeDecorator(messageDecorator); DecoratorPatternClient client = new DecoratorPatternClient(timeDecorator); client.execute(); }
client -> timeDecorator -> messageDecorator -> realComponent 순으로 호출이 진행된다.
실행을 해보면 위와 같이 결과가 나오게 되는데, 2개의 프록시가 모두 적용된 것을 확인할 수 있다.
의존 관계는 위의 사진과 같은데, 모두 Component 인터페이스를 구현하고 있는 것이다.
데코레이터 패턴도 프록시 패턴과 마찬가지로 클라이언트 코드가 프록시를 호출하는지, 실제 RealComponent를 호출하는지 모르며 클라이언트 코드를 변경하지 않아도 된다는 큰 장점이 있다.
Decorator 중간 인터페이스로 Decorator와 실제 기능 분리
Component를 구현하는 구현체 중에서 Decorator라는 인터페이스를 또 만들고 그것의 구현체들을 프록시로 두는 방법이 있다.
이 방법을 사용하면
1. 실제 기능, 데코레이터를 쉽게 구분 가능
2. 데코레이터의 중복 코드를 Decorator에 넣을 수 있음
'스프링 > 스프링 AOP' 카테고리의 다른 글
프록시를 이용한 로그추적기 - 구체클래스 기반 (0) 2023.09.10 프록시를 이용한 로그추적기 - 인터페이스 기반 (0) 2023.09.10 프록시 패턴 (0) 2023.09.10 프록시 - 프록시 패턴과 데코레이션 패턴이란? (0) 2023.09.10 템플릿 콜백 패턴 - 템플릿 패턴, 전략 패턴의 최종 (0) 2023.09.05