이번 글에서는 데코레이터 패턴에 대해서 알아보려고 합니다.
- 프록시와 데코레이터 패턴
- 사용예시
- 데코레이터 패턴이 적용되지 않은 예제코드
- 데코레이터 패턴이 적용된 예제코드
프록시와 데코레이터 패턴
프록시를 통하여 할 수 있는 기능은 크게 접근 제어와 부가기능 추가라는 2가지 기능으로 나뉘게 됩니다.
프록시 패턴을 포스팅할때, 프록시 패턴은 프록시를 이용한 접근 제어에 의도가 있었다면, 이번에 정리할 데코레이터 패턴은 프록시를 이용한 부가기능 추가에 그 의도가 있습니다.
GOF 디자인패턴에서는 프록시 패턴을 아래와 같이 정의하고 있습니다.
데코레이터 패턴(Decorator pattern)이란 주어진 상황 및 용도에 따라 어떤 객체에 책임을 덧붙이는 패턴으로, 기능 확장이 필요할 때 서브클래싱 대신 쓸 수 있는 유연한 대안이 될 수 있다.
사용예시
소개팅 어플을 서비스하고 있는 회사가 있다고 가정하자. 현재 서비스를 하는데 문제는 없지만, 매칭을 해주는 과정에서 부가적인 기능을 추가하려고 한다.
첫번째로는 매칭하는데 총 걸린 시간, 두번째로는 매칭이 되었을때 반환되는 데이터를 중간에 변경하여 반환하는 로직, 뿐만아니라 추후에 부가기능들이 추가될것을 고려하여 현재 매칭되는 로직을 설계해야한다.
개발자들은 데코레이션 패턴을 사용하여 재설계를 시작하려한다.
데코레이터 패턴이 적용되지 않은 예제코드
@Test
void noDecorator() {
Component realComponent = new RealComponent();
DecoratorPatternClient client = new DecoratorPatternClient(realComponent);
client.execute();
}
@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);
}
}
public interface Component {
String operation();
}
@Slf4j
public class RealComponent implements Component{
@Override
public String operation() {
log.info("RealComponent 실행");
return "data";
}
}
데코레이터 패턴 적용 전 - 클래스 의존 관계 |
여기까지는 프록시패턴과 비슷하여 이해하는데 어려움이 없을거라 생각하고 테스트코드 실행결과를 보여드리겠습니다.
데코레이터 패턴이 적용된 예제코드
데코레이터 패턴이 적용된 코드에서는 TimeDecorator클래스와 MessageDecorator클래스를 추가하였습니다.
@Test
void decorator2() {
Component realComponent = new RealComponent();
Component messageDecorator = new MessageDecorator(realComponent);
Component timeDecorator = new TimeDecorator(messageDecorator);
DecoratorPatternClient client = new DecoratorPatternClient(timeDecorator);
client.execute();
}
@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);
}
}
public interface Component {
String operation();
}
@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;
}
}
@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;
}
}
@Slf4j
public class RealComponent implements Component{
@Override
public String operation() {
log.info("RealComponent 실행");
return "data";
}
}
데코레이션 패턴 적용 후 - 클래스 의존 관계 |
데코레이션 패턴 적용 후 - 객체 의존 관계 |
예제 코드를 보면 아래와 같은 순서대로 진행하는 것을 알 수 있습니다.
- 테스트코드에서의 의존성 주입 후 execute()메서드 실행
- DecoratorPatternClient에서 생성자의 파라미터로 TimeDecorator가 주입되어 로그 부가기능 실행
- TimeDecorator에서 생성자의 파라미터로 MessageDecorator가 주입되어 응답값을 꾸며주는 부가기능 실행
- MessageDecorator에서 생성자의 파라미터로 RealComponent가 주입되어 소개팅 매칭기능 실행
그럼 테스트 코드를 실행해 보도록 하겠습니다. 부가기능이 모두 실행된 것을 볼 수 있습니다.
또한, 추후에 추가적인 기능이 생기더라도 기존의 코드의 변경없이 프록시 역할을 하는 클래스만 더 만들면 됩니다.
데코레이터 패턴은 프록시 패턴과 마찬가지로 프록시를 사용하는 패턴입니다. 그러한 이유로 사실 패턴의 모양도 같고 상황에 따라 너무 똑같아서 구별하지 못할 수도 있습니다. 단지, 의도에 따라서 사용되는 패턴이 다른 것 뿐입니다.
즉, 프록시를 사용하고 프록시가 접근 제어가 목적이라면 프록시 패턴이 되는것이고, 부가 기능추가에 목적이있다면 데코레이터 패턴이 되는 것입니다.
'디자인패턴' 카테고리의 다른 글
[디자인패턴] 프록시 패턴 (0) | 2021.11.24 |
---|---|
[디자인패턴] 전략 패턴 (0) | 2021.11.20 |
[디자인패턴] 템플릿 메서드 패턴 (0) | 2021.11.17 |