Decorator Pattern
기능을 확장할 때 기존 클래스를 서브 클래싱 하여 확장하는 경우 어떤 문제가 발생할까요?
앱 푸시 라이브러리를 개발한다고 생각해봅시다.
상위 클래스인 Notifier 클래스에는 여러 필드와 생성자가 있고, 이메일을 통해 알림을 전송하는 send 메소드가 있습니다.
사용자가 SMS, 카카오톡, 인스타그램, 슬랙 같은 채널을 통해서 알림을 받고 싶어 할 때 우리는 Notifier를 상속받는 SMS Notifier, Kakao Notifier 같은 하위 클래스로 확장할 수 있습니다.
그런데 만약 연결된 모든 채널로 부터 알림을 받거나 특정한 채널의 조합으로 알림을 받고 싶어 한다면요?
이런 방식으론 최악의 경우 모든 경우의 수에 맞춰 서브 클래싱 하게 될 수 있습니다.
여러 기능을 조합할 필요가 있을 때마다 하위 클래스가 생성되고 이는 라이브러리 코드를 무겁게 할 뿐 아니라 클라이언트 코드에도 영향을 끼칠 수 있습니다. 또한 상속은 정적이며 자식 클래스는 하나의 부모 클래스로부터 상속받을 수 있기 때문에 다른 대안이 필요합니다.
데코레이터 패턴은 기존 객체들을 새로운 행동을 수행하는 Wrapper 객체에 전달하여 기능을 확장하도록 하는 구조적 디자인 패턴입니다.
기능 확장이 필요할 때 하위 클래스를 확장하는 대신 유연하게 확장이 가능합니다.
장점
- 새로운 하위 클래스를 생성하지 않고도 객체의 기능을 유연하게 확장할 수 있습니다. (런타임 환경에서)
- 단일 책임 원칙을 위배하지 않고 객체 기능의 확장이 가능합니다.
단점
- 데코레이터 클래스가 많이 추가될 수 있고, 구조적으로 이해하기 어려워질 수 있습니다.
구현 방법
refactoring.guru의 예제를 활용해서 설명하겠습니다.
- 기능 확장의 대상이 되는 컴포넌트를 추출합니다.
- 구상 컴포넌트 클래스와 기본 데코레이터 클래스를 생성하고 구현합니다.
- 이때, 기본 데코레이터 클래스에는 컴포넌트를 참조할 필드가 추가돼야 합니다. 이는 생성자를 통해 전달하도록 합니다.
- 기본 데코레이터 클래스를 확장해서 구상 데코레이터 클래스를 생성합니다.
- 구상 데코레이터 클래스가 재정의할 메소드를 부모 메소드 호출 전 또는 후로 추가적인 행동을 수행하도록 구현합니다.
- 필요한 데코레이터들을 런타임에 Wrapping 해주는 역할은 클라이언트가 하도록 구현합니다.
어댑터(Adapter) vs. 프록시(Proxy) vs. 데코레이터(Decorator)
- 어댑터 : 기존 객체에 다른 인터페이스를 제공합니다.
- 프록시 : 기존 객체에 같은 인터페이스를 제공합니다.
- 데코레이터 : 기존 객체에 향상된 인터페이스를 제공합니다.
참고
'개발 > Design Pattern' 카테고리의 다른 글
[Design Pattern] 추상 팩토리 패턴 (Abstract Factory) (0) | 2022.10.26 |
---|---|
[Design Pattern] 팩토리 메서드 패턴 (Factory Method) (0) | 2022.10.25 |
[Design Pattern] 어댑터 패턴 (Adapter) (0) | 2022.10.24 |
[Design Pattern] 프록시 패턴 (Proxy) (0) | 2022.10.20 |
[Design Pattern] 싱글톤 패턴 (Singleton) (0) | 2022.10.19 |