본문 바로가기
프로그래밍/kotlin

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

by 뜨끔쓰 2022. 9. 19.
728x90
728x90
인프런의 김영한님의 강의 스프링 핵심 원리 - 고급편을 학습하며 정리한 글입니다.

 

스프링 핵심 원리 - 고급편 (인프런)

 

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

스프링의 핵심 원리와 고급 기술들을 깊이있게 학습하고, 스프링을 자신있게 사용할 수 있습니다., - 강의 소개 | 인프런...

www.inflearn.com

 

데코레이터 패턴이란?

데코레이터 패턴은 주어진 상황 및 용도에 따라 어떤 객체에 책임을 덧붙이는 패턴으로, 기능 확장이 필요할때 서브클래싱 대신 쓸 수 있는 유연한 대안이 될 수 있다. Feat.위키백과

 

데코레이터 패턴 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전.

ko.wikipedia.org

 

프록시 패턴과 비슷한데 프록시 패턴이 접근의 제어가 목적이라면 데코레이터 패턴의 경우 클라이언트와 서버의 요청 간의 새로운 기능일 추가하는 것이 목적인 경우가 데코레이터 패턴이다.

 

아무래도 코드로 확인 해보는 것이 이해가 더 빠를 것 같기 때문에 코드로 정리해봅시다.

 

기존 프록시패턴에서 보았던 예제 코드와 비슷합니다. 

 

Component라는 인터페이스를 작성합니다.

Component
interface Component {
    fun operation(): String?
}

별다른 기능은 없으며 operation이라는 함수를 선언해주었습니다.

 

이제 실제 로직으로 사용되는 RealComponent입니다.

RealComponent
class RealComponent: Component {
    private val log = LoggerFactory.getLogger(RealComponent::class.java)
    override fun operation(): String? {
        log.info("RealComponent 실행")
        return "data"
    }
}

Compoent 인터페이스를 상속받아 operation 함수를 재정의 하였습니다. 단순히 로그만 찍는 형태입니다.

 

이제 클라이언트 코드입니다.

DecoratorPatternClient
class DecoratorPatternClient(
    private val component: Component,
) {
    private val log = LoggerFactory.getLogger(DecoratorPatternClient::class.java)

    fun execute() {
        val result = component.operation()
        log.info("result={}", result)
    }
}

Component를 의존성으로 주입받아 operation함수를 실행하는 로직입니다.

 

데코레이터패턴이 포함되지 않은 기본 로직을 Test코드로 확인해봅시다!

 

noDecorator
    @Test
    fun noDecorator(){
        val realComponent = RealComponent()
        val client = DecoratorPatternClient(realComponent)
        client.execute()
    }

클라이언트 -> 서버 형태로 단순히 실행하는 테스트코드입니다.

 

결과

 

noDecorator 실행 결과

결과값은 별거 없이 RealComponent실행 로그가 찍히고 있습니다.

 

이제 우리는 Decorator패턴을 적용하여 클라이언트 단 코드를 바꾸지 않으면서 result결과 값을 바꿔보도록 합시다.

 

그러기 위해선 클라이언트와 서버간에 Proxy 클래스를 작성해줍시다.

 

MessageDecorator
class MessageDecorator(
    private val component: Component,
):Component {
    private val log = LoggerFactory.getLogger(MessageDecorator::class.java)
    override fun operation(): String? {
        log.info("MessageDecorator 실행")
        var result = component.operation()

        val decoResult = "*****${result}*****"
        log.info("MessageDecorator 꾸미기 적용 전 ={}, 적용 후 = {}", result, decoResult)
        return decoResult
    }
}

MessageDecorator입니다. Component를 의존성 주입 받아 operation함수를 재정의하면서 그안에서 주입받은 component의 operation을 실행해주고 반환받은 result값의 양옆에 *로 꾸며주고 있습니다.

 

이제 MessageDecorator 클래스를 이용하여 테스트 코드를 작성해보겠습니다.

 

decorator1
    @Test
    fun decorator1(){
        val realComponent = RealComponent()
        val messageDecorator = MessageDecorator(realComponent)
        val client = DecoratorPatternClient(messageDecorator)
        client.execute()
    }

코드를 보면 기존에는 DecoratorPatternClient가 RealComponent객체를 바로 주입받았다면 중간에 MessageDecorator클래스가 RealComponent객체를 주입받고 주입받은 messageDecorator객체를 다시 DecoratorPatternClient에서 주입받아 사용합니다.

 

그렇기 때문에 Client -> Proxy -> Server 순으로 Proxy가 가로채서 가공을 한뒤 Server로 넘겨준다고 볼 수 있습니다.

 

결과 값을 확인해봅시다.

 

결과

 

decorator1 결과

결과 값을 보시면 우리가 예상한대로 값이 변하고 log가 찍힌 것을 확인 할 수 있습니다.

 

 

이렇게 데코레이터 패턴을 이용하면 기존 클라이언트단 코드는 건드리지 않고 원하는 데이터 가공을 할 수 있습니다.

 

간단히 장단점에 대해 알아보도록 합시다.


장점

  • 클라이언트단 코드를 수정하지 않고도 추가 기능들을 확장 할 수 있습니다.
  • 구성과 위임을 통하여 계속 이어 붙여서 추가기능을 확장 할 수 있습니다.

단점

  • 아무래도 코드가 복잡해지기 때문에 가독성의 문제가 생길 수 있습니다.

 

이렇게 배운 내용들을 정리하였는데 지금 당장은 이해가되고 어떤 느낌인지 잘 알지만 까먹었을때 주기적으로 복습이 필요 할 것 같다!

 

글을 읽어주셔서 감사합니다. 잘못된 정보나 추가가 필요한 경우 댓글 남겨주시면 감사하겠습니다.

 

 

728x90
반응형

댓글