반응형

데코레이터 패턴(Decorator Pattern)

  - 데코레이터 패턴은 프록시 기법을 사용하는 디자인 패턴 중에 하나입니다.

  - GOF 디자인 패턴에서는 의도에 따라 두 가지 패턴으로 구분하였습니다. 

 

프록시의 주요 기능

프록시를 통해서 할 수 있는 일은 크게 2가지로 나뉜다.

1. 접근 제어 (권한에 따른 접근 차단, 캐싱 등)

2. 부가 기능 추가 (다른 로직을 추가하는 등의 부가 기능을 수행, 예) 실행 시간을 측정해서 로그를 남긴다.)

 

* GOF 디자인 패턴에서는 의도에 따라서 프록시 패턴과, 데코레이터 패턴으로 구분한다.

  - 프록시 패턴 : 접근 제어가 목적

  - 데코레이터 패턴 : 새로운 기능 추가가 목적

 

프록시 패턴의 장점

  - 부가 기능을 추가하여서 원하는 흐름에 맞게 조정할 수 있습니다.

 

코드

 * 메시지를 출력하는 기능에 메시지를 꾸며주는 기능을 데코레이터 패턴을 이용해서 적용해 보겠습니다.

 

Component.java

  - interface로 Component 생성

 

public interface Component {
    String operation();

}

 

RealComponent.java

  - 실제 Component 구현체

 

@Slf4j
public class RealComponent implements Component{
    @Override
    public String operation() {
        log.info("RealComponent 실행");
        return "data";
    }
}

 

DecoratorPatternClient.java

  - Component를 실행하는 클라이언트 클래스

 

@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.java

  - 기본 메시지 출력 기능 테스트

@Slf4j
public class DecoratorPatternTest {

    @Test
    void noDecorator() {
        Component realComponent = new RealComponent();
        DecoratorPatternClient client = new DecoratorPatternClient(realComponent);
        client.execute();
    }
}

결과 : RealComponent 실행

          result=data

 

MessageDecorator.java

  - 데코레이터 패턴을 이용해서 메시지 꾸며주는 기능 추가

  - MessageDecorator는 Component 인터페이스를 구현한다.

 

@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;
    }
}

 

DecoratorPatterTest.java

  - 클라이언트가 호출하기 전에 MessageDecorator 클래스를 이용해서 부가 기능을 추가 후 클라이언트에게 messageDecorator를 넘겨준다. messageDecorator도 Component를 구현하고 있기 때문에 클라이언트 입장에서는 전혀 문제가 발생하지 않는다.

 

@Slf4j
public class DecoratorPatternTest {

    @Test
    void decorator1() {
        Component realComponent = new RealComponent();
        Component messageDecorator = new MessageDecorator(realComponent);
        DecoratorPatternClient client = new DecoratorPatternClient(messageDecorator);
        client.execute();
    }
}

 

결과 :

 

정리 :

 

데코레이터 패턴의 의도 : 객체에 추가 책임(기능)을 동적으로 추가하고, 기능 확장을 위한 유연한 대안 제공

프록시 패턴의 의도 : 다른 객체에 대한 접근을 제어하기 위해 대리자를 제공

728x90
반응형
반응형

프록시 패턴(Proxy Pattern)

  - Proxy는 대리자, 대변인 이라는 뜻이다. 

  - 프록시 패턴은 구조패턴(Structural Pattern) 중 하나이다.
  - 구조 패턴은 작은 클래스들을 상속과 합성을 이용하여 더 큰 클래스를 생성하는 방법을 제공하는 패턴이다.

  - 인터페이스나 구현을 복합하는 것이 아니라 객체를 합성하는 방법을 제공한다.

    이는 컴파일 단계에서가 아닌 런타임 단계에서 복합 방법이나 대상을 변경할 수 있다는 점에서 유연성을 갖는다.

 

프록시의 주요 기능

프록시를 통해서 할 수 있는 일은 크게 2가지로 나뉜다.

1. 접근 제어 (권한에 따른 접근 차단, 캐싱 등)

2. 부가 기능 추가 (다른 로직을 추가하는 등의 부가 기능을 수행, 예) 실행 시간을 측정해서 로그를 남긴다.)

 

* GOF 디자인 패턴에서는 의도에 따라서 프록시 패턴과, 데코레이터 패턴으로 구분한다.

  - 프록시 패턴 : 접근 제어가 목적

  - 데코레이터 패턴 : 새로운 기능 추가가 목적

 

프록시 패턴의 장점

  - 제어의 흐름을 변경하거나 다른 로직을 수행하기 위해 사용한다.

  - 객체에 대하여 접근할 때에 Wrapper Class를 두어 접근에 대한 통제를 위해 사용된다.

 

코드

  * 더하기 빼기 계산기 로직을 구현 예제 (프록시 패턴을 이용해서 계산 시간을 출력)

 

  - 계산기 interface

 

public interface Calculator {

    public int plus(int left, int right);
    public int subtract(int left, int right);

}

 

  - 계산기 interface 구현 service

 

public class CalculatorService implements Calculator{

    private int result;

    @Override
    public int plus(int left, int right) {
        result = left + right;
        return result;
    }

    @Override
    public int subtract(int left, int right) {
        result = left - right;
        return result;
    }
}

 

  - 프록시 패턴을 사용하지 않은 테스트

 

@SpringBootTest
class CalculatorServiceTest {

    @Test
    void calculatorService() {

        Calculator calculatorService = new CalculatorService();

        int left = 10000;
        int right = 5000;

        int plusResult = calculatorService.plus(left, right);
        int subtractResult = calculatorService.subtract(left, right);

        assertEquals(plusResult, left+right);
        assertEquals(subtractResult, left-right);

    }

}

 

  - 프록시 패턴 클래스

 

public class CalculatorServiceProxy implements Calculator{

    private int result;

    @Override
    public int plus(int left, int right) {
        long beforeTime = System.currentTimeMillis();

        result = left + right;
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long afterTime = System.currentTimeMillis();
        long secDiffTime = (afterTime - beforeTime)/1000;
        System.out.println(" plus 계산 시간(m) : "+secDiffTime);

        return result;
    }

    @Override
    public int subtract(int left, int right) {
        long beforeTime = System.currentTimeMillis();

        result = left - right;
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long afterTime = System.currentTimeMillis();
        long secDiffTime = (afterTime - beforeTime)/1000;
        System.out.println("subtract 계산 시간(m) : "+secDiffTime);

        return result;
    }
}

 

- 프록시 패턴을 사용한 테스트

 

@SpringBootTest
class CalculatorServiceTest {

    @Test
    void calculatorServiceProxy() {

        Calculator calculatorServiceProxy = new CalculatorServiceProxy();

        int left = 10000;
        int right = 5000;

        int plusResult = calculatorServiceProxy.plus(left, right);
        int subtractResult = calculatorServiceProxy.subtract(left, right);

        assertEquals(plusResult, left+right);
        assertEquals(subtractResult, left-right);

    }


}

 

결과 : plus 계산 시간(m) : 1
        subtract 계산 시간(m) : 2

 

 

정리

 - 프록시 패턴의 의도 : 다른 객체에 접근을 제어하기 위해 대리자를 제공

 - 테스트 코드를 보면 생성 객체만 바꿈으로서 Calculator interface는 영향을 받지 않게 Proxy 클래스를 통해서 원하는 목적에 맞게 흐름을 조정할 수 있었다. 해당 패턴은 Spring 프레임워크에 AOP 동작원리에 사용되어지고 있다.

 * 중요한 점은 흐름제어만 할 뿐 결과값을 조작하거나 변경시키면 안된다.

 

참조 

 - https://readystory.tistory.com/132?category=822867

728x90
반응형

+ Recent posts