ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring Core - 데코레이터 패턴 (예제1, 예제2, 예제3)
    FrameWork/Spring&Spring-boot 2024. 3. 11. 12:23

    데코레이터 패턴 - 예제 코드1

     

     

    Component 인터페이스

    public interface Component {
        String operation();
    }
    

     

    Component 인터페이스는 단순히 String operation()메서드를 가진다.

     

    RealComponent

    package com.core.springproxy.pureproxy.decorator.code;
    
    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j
    public class RealComponent implements Component{
        @Override
        public String operation() {
            log.info("RealComponent 실행");
            return "data";
        }
    }
    

     

    • RealComponent는 Component 인터페이스를 구현한다.
    • operation() : 단순히 로그를 남기고 "data" 문자를 반환한다.

     

    DecoratorPatternClient

    package com.core.springproxy.pureproxy.decorator.code;
    
    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j
    public class RealComponent implements Component{
        @Override
        public String operation() {
            log.info("RealComponent 실행");
            return "data";
        }
    }
    

     

    • 클라이언트 코드는 단순히 Component 인터페이스 타입의 필드하나를 가진다.
    • exexute를 실행하면 component.operation()을 호출하고, 그 결과를 출력한다.

     

    DecoratorPatternTest

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

     

    테스트 코드는 client → realComponent의 의존관계를 설정하고, client.execute()를 호출한다.

     

     

    실행결과


    데코레이터 패턴 - 예제 코드2

    부가 기능 추가

    앞서 설명한 것 처럼 프록시를 통해서 할 수 있는 기능은 크게 접근 제어부가 기능 추가라는 2가지로 구분한다. 앞서 프록시 패턴에서 캐시를 통한 접근 제어를 알아보았다. 이번에는 프록시를 활용해서 부가 기능을 추가해보자. 이렇게 프록시로 부가 기능을 추가하는 것을 데코레이터 패턴이라 한다.

     

    데코레이터 패턴 : 원래 서버가 제공하는 기능에 더해서 부가 기능을 수행한다.

    • 예) 요청 값이나, 응답 값을 중간에 변형한다.
    • 예) 실행 시간을 측정해서 추가 로그를 남긴다.

     

    응답 값을 꾸며주는 데코레이터 

    응답 값을 꾸며주는 데코레이터 프록시를 만들어보자.

     

    MessageDecorator

    package com.core.springproxy.pureproxy.decorator.code;
    
    import lombok.extern.slf4j.Slf4j;
    
    @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;
        }
    }
    

     

     

    DecoratorPatternTest - 추가

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

     

    client → messageDecorator → realComponent 의 객체 의존 관계를 만들고 client.execute()를 호출한다.

     

    실행 결과

     

    실행 결과를 보면 MessageDecorator가 RealComponent를 호출하고 반환한 응답 메시지를 꾸며서 반환한 것을 확인할 수있다. 


    데코레이터 패턴 - 예제 코드3

    데코레이터 기능에서 체인은 어떤 제품에서 client에서 한 요청이 메인로직에 도달하기 전에 계속 추가적인 기능을 넣는 것이다.

     

    TimeDecorator

    package com.core.springproxy.pureproxy.decorator.code;
    
    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j
    public class TimeDecorator implements Component{
    
    
        Component messageDecorator;
    
    
        public TimeDecorator(Component messageDecorator) {
            this.messageDecorator = messageDecorator;
    
        }
    
        @Override
        public String operation() {
            log.info("TimeDecorator 실행");
            long startTime =  System.currentTimeMillis();
    
            String result = messageDecorator.operation();
    
            long endTime = System.currentTimeMillis();
            long resultTime = endTime - startTime;
            log.info("소요 시간 = {}ms",resultTime);
            return result;
    
        }
    }
    

     

    TimeDecorator는 실행 시간을 측정하는 부가 기능을 제공한다. 대상을 호출하기 전에 시간을 가지고 있다가, 대상의 호출이 끝나면 호출 시간 로그를 남겨준다.

     

    DecoratorPatternTest추가

    @Test
    void decorator2(){
        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 의 객체 의존관계를 설정하고, 실행한다.

     

     

    실행결과

     

    실행 결과를 보면 TimeDecorator가 MessageDecorator를 실행하고 실행 시간을 측정해서 출력한 것을 확인 할 수 있다. 


    프록시 패턴과 데코레이터 패턴 정리

    GOF 데코레이터 패턴 

    여기서 생각해보면 Decorator기능에 일부 중복이 있다. 꾸며주는 역할을 하는 Decorator들은 스스로 존재 수 없다. 항상 꾸며줄 대상이 있어야 한다. 따라서 내부에 호출 대상인 component를 가지고 있어햐 한다. 그리고 component를 항상 호출해야 한다. 이 부분이 중복된다. 이런 중복을 제거하기 위해 component를 속성으로 가지고 있는 Decorator라는 추상 클래스를 만든튼 방법도 고민할 수 있다. 이렇게 하면 추가로 다이어그램에서 어떤 것이 실제 컴포넌트 인지, 데코레이터인지 구분 할 수 있다. 여기까지 고민한 것이 바로 GOF에서 설명하는 데코레이터 패턴의 기본 예제이다.

     

     

    프록시 패턴  VS  데코레이터 패턴

     여기까지 진행하면 몇가지 의문이 들 것이다.

    • Decorator라는 추상 클래스를 만들어야 데코레이터 패턴일까?
    • 프록시 패턴과 데코레이터 패턴은 그 모양이 거의 비슷한 것 같은데?

     

    의도(intent)

    사실 프록시 패턴과 데코레이터 패턴은 그 모양이 거의 같고, 상황에 따라 정말 똑같을 때도 있다. 그러면 둘을 어떻게 구분하는 것일까? 디자인 패턴에서 중요한 것은 해당 패턴의 겉모양이 아니라 그 패턴을 만든 의도가 더 중요하다. 따라서 의도에 따라 패턴을 구분한다.

     

    • 프록시 패턴의 의도 : 다른 개체에 대한 접근을 제어 하기 위해 대리자를 제공
    • 데코레이터 패턴의 의도 : 객체에 추가 책임(기능)을 동적으로 추가 하고, 기능 확장을 위한 유연한 대안 제공

     

    정리

    프록시를 사용하고 해당 프록시가 접근 제어가 목적이라면 프록시 패턴이고, 새로운 기능을 추가하는 것이 목적이라면 데코레이터 패턴이된다. 

     

     

    [출처 - 스프링 핵심원리-고급편, 저 김영한]

    https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B3%A0%EA%B8%89%ED%8E%B8

     

    스프링 핵심 원리 - 고급편 강의 - 인프런

    스프링의 핵심 원리와 고급 기술들을 깊이있게 학습하고, 스프링을 자신있게 사용할 수 있습니다., 핵심 디자인 패턴, 쓰레드 로컬, 스프링 AOP스프링의 3가지 핵심 고급 개념 이해하기 📢 수강

    www.inflearn.com

     

    댓글

Designed by Tistory.