알림 라이브러리를 만들고 있다 가정하자. Notifier 인터페이스를 만들었을 때는 이메일 전송을 하는 기능밖에 없었다. 나중에 사용자들이 SMS나 SNS를 통해서도 알림을 받고 싶어한다. 어떤이는 두개 이상의 알림을 받고싶어한다. 이런 알림을 SMS+SNS, SMS+이메일 식으로 추가하다보니 지나치게 많은 클래스들이 만들어지게 된다.

해결

클래스의 확장을 고려해야하지만 주의사항이 있다.

  • 상속은 정적이다 런타임 때 기존 객체의 행동을 변경할 수 없다. 전체 객체를 다른 자식 클래스에서 생성된 다른 객체로만 바꿀 수 있다.
  • 자식 클래스는 하나의 부모 클래스만 가질 수 있다. 대부분 언어에서의 상속은 클래스가 동시에 여러 클래스의 행동을 상속하도록 허용하지 않는다.

이러한 상황에서 극복을 하는 방법은 상속 대신 집합 관계 또는 합성을 이용하는 것이다. 집합 관계는 다른 한객체가 다른 객체에 대해 참조를 갖고 일부 작업을 위임하지만, 상속을 하면 객체 자체가 부모 클래스의 행동을 상속한 후 해당 작업을 수행할 수 있다.

이러한 방법을 사용하면 도우미 객체를 다른 객체로 쉽게 대체하여 런타임 때 컨테이너의 행동을 변경할 수 있다. 객체는 여러 클래스의 행동들을 사용할 수 있고, 여러 객체에 대한 참조들이 있으며 이 객체들에 모든 종류의 작업을 위임한다. 집합 관계/합성은 데코레이터를 포함한 많은 디자인 패턴의 핵심 원칙이다.

상속과 집합 관계의 차이점.

래퍼는 데코레이터 패턴의 별명이다. 래퍼는 일부 대상 객체와 연결할 수 있는 개체이다. 래퍼에는 대상 객체와 같은 메서드들의 집합이 포함되어 있고, 래퍼는 자신이 받는 요청을 대상 객체에 위임하지만 래퍼는 이 요청을 대상에 전달하기 전, 후로 무언가를 수행할 수 있다.

래퍼의 참조 필드가 해당 인터페이스를 따르는 모든 객체를 받도록 한다. 이렇게 하면 여러 래퍼로 객체를 포장해서 모든 래퍼들의 합성된 행동들을 객체에 추가할 수 있다.

데코레이터 패턴을 사용한 해결책

클라이언트 코드는 기초 알람자 객체를 클라이언트의 요구사항들과 일치하는 데코레이터들의 집합으로 래핑해야 한다. 위 결과 객체들은 스택으로 구성된다.

앱들은 알림 데코레이터들의 복잡한 스택들을 설정할 수 있습니다

마지막 데코레이터는 실제로 클라이언트와 작업하는 객체이다. 모든 데코레이터들은 기초알림자와 같은 인터페이스를 구현하므로 나머지 클라이언트 코드는 순순한 알림자 객체와 작동하든 데코레이터로 작동하든 신경쓰지 않는다.

구조

데코레이터 디자인 패턴 구조

  • 컴포넌트는 래퍼들과 래핑된 객체들 모두에 대한 공통 인터페이스를 선언한다
  • 구성 컴포넌트는 래핑되는 객체들의 클래스이다.
  • 기초 데코레이터는 클래스에는 래핑된 객체를 참조하기 위한 필드가 선언되어 있다. 필드 유형은 구상 컴포넌트들과 구상 데코레이터를 모두 포함할 수 있다.
  • 구상 데코레이터들은 컴포넌트들에 동적으로 추가될 수 있는 추가 행동들을 정의한다.

장단점

  • 새 자식 클래스를 만들지 않고도 객체의 행동을 확장할 수 있다
  • 런타임에 객체들에서부터 책임들을 추가하거나 제거할 수 있다
  • 객체를 여러 데코레이터로 래핑하여 여러 행동들을 합성할 수 있다
  • 단일 책임 원칙을 준수한다
  • 래퍼들의 스택에서 특정 래퍼를 제거하기 어렵다
  • 데코레이터 스택 내의 순서에 의존하지 않는 방식으로 데코레이터를 구현하기가 어렵다
  • 계층들의 초기 설정 코드가 보기 흉할 수 있다