본문 바로가기
디자인 패턴

데코레이터 패턴

by oncerun 2022. 1. 21.
반응형

 

이전에 공부했던 프락시 패턴의 핵심은 프락시를 도입해서 기존 코드의 영향을 주지 않고 접근 제어를 했다는 점이다. 또한 해당 프락시를 체인을 통해 추가할 수도 혹은 제거하여 구현체를 의존하도록 할 수도 있다. 

 

데코레이터 패턴은 원래 서버가 제공하는 기능에 더해서 부가 기능을 수행하는 의도로 구현하는데, 예를 들어 요청 혹은 응답 값을 중간에 변형하거나, 특정 로그를 남기는 등 다양한 기능을 추가할 수 있다.

 

 

클라이언트 객체는 외부 객체와 협력하기위해 메시지를 주고받으며 동작한다.

이 과정에서 보통 다형성을 위해 인터페이스 혹은 상속을 많이 사용하는데, 인터페이스가 없는 경우를 생각해 보자.

 

 

1. Client가 Server를 직접 호출하는 경우에는 다음과 같은 의존성이 표현되어있다.

@Slf4j
public class Client {

    private final Server server ;

    public Client(Server server) {
        this.server = server;
    }
    public void logic(){
        log.info("Client 호출");
        server.execute();
    }

}

 

 

클라이언트는 프록시의 사용 여부를 몰라야 하며, 클라이언트의 코드 수정 없이 데코레이터 패턴이 도입되어야 한다.

 

이 경우에는 Server라는 구현체에 직접의존되어 있으며 해당 Server객체는 인터페이스를 사용하지 않았다. 

Server의 execute()가 실행될 때 실행 시간을 측정하고 싶은데 부가 기능을 추가하기 때문에 데코레이터 패턴을 적용해 진행해볼 수 있다.

 

2. Server에 대응되는 Proxy객체를 생성한다.

@Slf4j
public class ProxyServer extends Server{

    private final Server target;

    public ProxyServer(Server target) {
        this.target = target;
    }

    @Override
    public void execute() {

        long startTime = System.currentTimeMillis();

        target.execute();

        long endTime = System.currentTimeMillis();

        long resultTime = endTime - startTime;

        log.info("result Time = {} ", resultTime);


    }
}

 

자바의 다형성을 표현하는 방법에는 인터페이스뿐만 아니라 상속이라는 개념도 있다.  부모 타입에 자식 타입을 주입할 수 있기 때문이다.  분명 상속이 가지는 단점도 존재하지만 의존하는 객체가 다형성을 적용할 수 없거나, 확장 여부가 없을 때나 다른 상속을 받지 않아도 되는 객체인 경우 간단히 적용해 볼 수 있다. 

 

* 해당 예제에는 보이지 않지만 extends의 부모 클래스에서 생성자 주입을 여러 객체를 받는 경우에는 자바 문법에 따라 부모의 생성자를 호출하여야한다. 만약 Proxy에서 부모에서 주입받는 객체들을 사용하지 않는 경우라면 null을 넣어서 마무리해야 한다.

public ProxyServer(Server target) {
    super(null);
    this.target = target;
}

 

 

@Slf4j
public class Client {

    private final Server server ;

    public Client(Server server) {
        this.server = server;
    }
    public void logic(){
        log.info("Client 호출");
        server.execute();
    }

}
@Slf4j
public class Client {

    private final Server server ;

    public Client(ProxyServer server) {
        this.server = server;
    }
    public void logic(){
        log.info("Client 호출");
        server.execute();
    }

}

해당 코드에서 주입받을 때 구현체가 아닌 프락시를 주입하고 있다. 스프링 프레임워크에서 DI 주입을 넣는 설정 파일에서 주입되는 부분을 변경하면 된다.  이때 주의할 점은 ProxyServer인 프락시는 실제 target인 Server를 주입받아야 한다는 점이다. 

 

 

이로인해 다음과 같이 의존관계가 설정되었다.
Client - > Server
Client ->ProxyServer ->Server


 

 

GOF 데코레이터 패턴에서 Decorator 기능을 추가하다 보면 중복되는 코드가 발생하는데

바로 target을 받는 부분이다. 

데코레이터는 꾸며주는 역할을 하지 스스로 독립적인 존재가 될 수 없기 때문이며 내부 호출 대상인 target을 가지고 있어야 하며 target을 항상 호출해야만 한다.

이러한 중복을 제거하기 위해 target의 속성을 가지고 있는 추상 클래스를 만드는 방법 또한 고민할 수 있다. 

이렇게 변경하면 추가로 클래스 다이어그램에서 어떤 것이 실제 target이고 decorator인지 명확하게 구분할 수 있으며, 이 부분까지 고민한 것이 바로 GOF에서 설명하는 데코레이터 패턴 예제이다.

 

반응형

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

데코레이터 패턴 + [F]  (0) 2022.02.19
빌더 패턴 + [F]  (0) 2022.02.19
프록시 패턴  (0) 2022.01.20
전략 패턴  (0) 2022.01.17
템플릿 메서드 패턴  (0) 2022.01.15

댓글