본문 바로가기
Design Pattern

9. 데코레이터 패턴(Decorator pattern)

by 킹차니 2021. 12. 21.

 

데코레이터 패턴

기존 코드를 변경하지 않고, 부가 기능을 추가하는 패턴

상속이 아닌 위임을 사용하여 보다 유연하게(런타임에) 부가 기능을 추가하는 것도 가능하다.

 

 

 


 

코드로 살펴보기

 

어떤 웹 사이트에서 사용자들이 작성한 Comment를 관리하는 어플리케이션이 있다고 해보자.

Comment를 관리하는 CommentService는 아래와 같다.

단순히 addComment는 Comment를 출력하는 메소드이다.(간단하게 하기 위해 이것을 관리하는 거라고 한다..)

 

그런데 만약 " ... " 과 같은 특수기호는 관리하고 싶지 않아서 그에 맞는 기능을 추가한 TrimmingCommentService는 아래와 같다.

 

이제 이를 사용하는 Client를 보자.

실행 결과는 아래와 같다.

 

하지만 이때 광고를 걸러내기 위한 새로운 서비스인 SpamFilteringCommentService가 필요해진다.

이전과 같이 CommentService를 상속하여 만든다.

 

 

 

이를 사용하기 위해서는 아래와 같이 의존관계만 바꿔주면 된다.

실행결과:

trim은 적용되지 않았지만 http가 포함된 comment는 걸러진 것을 볼 수 있다.

 

❗️문제 발생 ❗️

그런데 만약 SpamService도 사용하고, TrimService도 사용한다면 SpamService의 코드와 TrimService 코드가 모두 포함된 새로운 CommentService를 만들어야 한다. 아니면 새로운 CommentService를 만들어서 SpamService도 상속하고, TrimService도 상속하는 이중상속을 해야하지만 자바는 다중 상속을 지원하지 않는다.

이를 데코레이터 패턴을 적용하여 해결해보자.

 

먼저 공통된 기본 기능을 가진 ComponentCommentService 인터페이스

 

그리고 그 기본 기능을 구현하는 ConcreteComponent에 속하는 DefaultCommentDecorator

 

다음으로 Decorator에 속하는 CommentDecorator

CommentDecorator는 CommentService를 참조로 가지는 것을 볼 수 있다. 이것을 wrappee라고 할 수 있는데, 다양한 Decorarot를 참조하기 위해 반드시 필요하다.

 

이제 ConcreteDecorator들을 보자.

TrimmingCommentDecorator

 

SpamFilteringCommentDecorator

 

DateCommentDecorator

 

Client는 아래와 같다.

CommentService Component를 사용하는 것을 볼 수 있다.

 

이제 이를 main에서 실행해보자.

실행결과:

모든 Decorator가 적용된 것을 볼 수 있다.

 

디버깅을 해보면

이렇게 객체의 결합을 통해 다양한 Decorator들이 적용된다는 것을 알 수 있다.

 

 


 

장점과 단점

장점

1. 새로운 클래스를 만들지 않고 기존 기능을 조합할 수 있다.

2. 컴파일 타임이 아닌 런타임에 동적으로 기능을 변경할 수 있다.

3. 현재 Client가 인터페이스를 사용함으로써 DI원칙을 적용할 수 있다.

 

단점

데코레이터를 조합하는 코드가 복잡할 수 있다.

 

 


 

실제로 어디에 사용되나?

 

1. 자바에서

 

 

2. 스프링에서

 

 

출처: 인프런 백기선님 '코딩으로 학습하는 GoF 강의'
https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4