인프런의 김영한님의 강의 스프링 핵심 원리 - 고급편을 학습하며 정리한 글입니다.
데코레이터 패턴이란?
데코레이터 패턴은 주어진 상황 및 용도에 따라 어떤 객체에 책임을 덧붙이는 패턴으로, 기능 확장이 필요할때 서브클래싱 대신 쓸 수 있는 유연한 대안이 될 수 있다. Feat.위키백과
프록시 패턴과 비슷한데 프록시 패턴이 접근의 제어가 목적이라면 데코레이터 패턴의 경우 클라이언트와 서버의 요청 간의 새로운 기능일 추가하는 것이 목적인 경우가 데코레이터 패턴이다.
아무래도 코드로 확인 해보는 것이 이해가 더 빠를 것 같기 때문에 코드로 정리해봅시다.
기존 프록시패턴에서 보았던 예제 코드와 비슷합니다.
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()
}
클라이언트 -> 서버 형태로 단순히 실행하는 테스트코드입니다.
결과
결과값은 별거 없이 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로 넘겨준다고 볼 수 있습니다.
결과 값을 확인해봅시다.
결과
결과 값을 보시면 우리가 예상한대로 값이 변하고 log가 찍힌 것을 확인 할 수 있습니다.
이렇게 데코레이터 패턴을 이용하면 기존 클라이언트단 코드는 건드리지 않고 원하는 데이터 가공을 할 수 있습니다.
간단히 장단점에 대해 알아보도록 합시다.
장점
- 클라이언트단 코드를 수정하지 않고도 추가 기능들을 확장 할 수 있습니다.
- 구성과 위임을 통하여 계속 이어 붙여서 추가기능을 확장 할 수 있습니다.
단점
- 아무래도 코드가 복잡해지기 때문에 가독성의 문제가 생길 수 있습니다.
이렇게 배운 내용들을 정리하였는데 지금 당장은 이해가되고 어떤 느낌인지 잘 알지만 까먹었을때 주기적으로 복습이 필요 할 것 같다!
글을 읽어주셔서 감사합니다. 잘못된 정보나 추가가 필요한 경우 댓글 남겨주시면 감사하겠습니다.
'프로그래밍 > kotlin' 카테고리의 다른 글
[Spring] 프록시 팩토리 (0) | 2022.09.26 |
---|---|
[KOTILIN] JDK 동적 프록시 (0) | 2022.09.25 |
[디자인 패턴] 프록시 패턴이란 (0) | 2022.09.19 |
[디자인 패턴] 전략 패턴이란 (0) | 2022.09.15 |
[디자인 패턴] 템플릿 메서드 패턴이란 (0) | 2022.09.14 |
댓글