본문 바로가기

디자인패턴

[디자인패턴] 데코레이터 패턴

이번 글에서는 데코레이터 패턴에 대해서 알아보려고 합니다.

 

  • 프록시와 데코레이터 패턴
  • 사용예시
  • 데코레이터 패턴이 적용되지 않은 예제코드
  • 데코레이터 패턴이 적용된 예제코드
프록시와 데코레이터 패턴

프록시를 통하여 할 수 있는 기능은 크게 접근 제어부가기능 추가라는 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";
    }
}

데코레이션 패턴 적용 후 - 클래스 의존 관계

데코레이션 패턴 적용 후 - 객체 의존 관계

예제 코드를 보면 아래와 같은 순서대로 진행하는 것을 알 수 있습니다.

  1. 테스트코드에서의 의존성 주입 후 execute()메서드 실행
  2. DecoratorPatternClient에서 생성자의 파라미터로 TimeDecorator가 주입되어 로그 부가기능 실행
  3. TimeDecorator에서 생성자의 파라미터로 MessageDecorator가 주입되어 응답값을 꾸며주는 부가기능 실행
  4. MessageDecorator에서 생성자의 파라미터로 RealComponent가 주입되어 소개팅 매칭기능 실행

그럼 테스트 코드를 실행해 보도록 하겠습니다. 부가기능이 모두 실행된 것을 볼 수 있습니다.

또한, 추후에 추가적인 기능이 생기더라도 기존의 코드의 변경없이 프록시 역할을 하는 클래스만 더 만들면 됩니다.

 

데코레이터 패턴은 프록시 패턴과 마찬가지로 프록시를 사용하는 패턴입니다. 그러한 이유로 사실 패턴의 모양도 같고 상황에 따라 너무 똑같아서 구별하지 못할 수도 있습니다. 단지, 의도에 따라서 사용되는 패턴이 다른 것 뿐입니다.

즉, 프록시를 사용하고 프록시가 접근 제어가 목적이라면 프록시 패턴이 되는것이고, 부가 기능추가에 목적이있다면 데코레이터 패턴이 되는 것입니다.

'디자인패턴' 카테고리의 다른 글

[디자인패턴] 프록시 패턴  (0) 2021.11.24
[디자인패턴] 전략 패턴  (0) 2021.11.20
[디자인패턴] 템플릿 메서드 패턴  (0) 2021.11.17