데코레이터 패턴이란?
구조 패턴 중 하나
객체에 추가 요소를 동적으로 더할 수 있다
서브클래스를 만들 때보다 더 유연하게 기능을 확장할 수 있다
패턴 적용 전 코드
public interface Notifier {
void send();
}
public class BasicNotifier implements Notifier {
@Override
public void send() {
System.out.println("기본 알림");
}
}
public class EmailNotifier implements Notifier {
@Override
public void send() {
System.out.println("이메일 알림");
}
}
알림 기능을 위한 인터페이스를 하나 정의해보자
- BasicNotifier 클래스는 기본 알림 구현
- EmailNotifier 클래스는 이메일 알림을 구현
public class Application {
public static void main(String[] args) {
Notifier notifier = new BasicNotifier();
notifier.send();
System.out.println("--end--");
Notifier emailNotifier = new EmailNotifier(new BasicNotifier());
emailNotifier.send();
System.out.println("--end--");
}
}
-------------------------------------------
기본 알림
--end--
이메일 알림
--end--
하지만 이메일 알림과 기본 알림을 동시에 해야하는 요구사항이 생겼을 때
어떻게 처리를 하는 것이 좋을까?
여기서 프록시 패턴을 생각할 수도 있지만 데코레이터 패턴을 적용할 것이다
패턴 적용 후
public interface Notifier {
void send();
}
public class BasicNotifier implements Notifier {
@Override
public void send() {
System.out.println("기본 알림");
}
}
public class EmailNotifier implements Notifier {
private Notifier notifier;
public EmailNotifier(Notifier notifier) {
this.notifier = notifier;
}
@Override
public void send() {
notifier.send();
System.out.println("이메일 알림");
}
}
// 문자 알림도 추가
public class SmsNotifier implements Notifier {
private Notifier notifier;
public SmsNotifier(Notifier notifier) {
this.notifier = notifier;
}
@Override
public void send() {
notifier.send();
System.out.println("문자 알림");
}
}
메인메소드에 실행을 시켜보면
public class Application {
public static void main(String[] args) {
// 1. 기본 알림 -> 이메일 알림 -> 문자 알림
Notifier allNotifier = new SmsNotifier(new EmailNotifier(new BasicNotifier()));
allNotifier.send();
System.out.println("--end--");
// 2. 기본 알림 -> 문자 알림 -> 이메일 알림
Notifier allNotifier2 = new EmailNotifier(new SmsNotifier(new BasicNotifier()));
allNotifier2.send();
System.out.println("--end--");
}
}
----------------------------------------------
기본 알림
이메일 알림
문자 알림
--end--
기본 알림
문자 알림
이메일 알림
--end--
이렇게 요구사항에 맞게 알림을 다양하게 줄 수 있게 되었다
아까 전에 프록시 대신 데코레이터를 선택한 이유는?
프록시 패턴은 접근에 대해 제어를 목적으로 하고
데코레이터 패턴은 기존 기능에 다른 기능을 추가하는 목적이기 때문이다
프록시 패턴과 공통점과 차이점
공통점
- 둘 다 interface 를 구현
차이점
- 프록시 패턴은 패턴이 적용된 클래스들의 관계가 컴파일 시에 정해짐
- 데코레이터 패턴은 런타임 시에 정해짐
데코레이터 패턴을 적용했지만 해결하지 못한 문제점
이메일 알림과, 문자 알림 이 두 알림 중에 하나만 사용하고 싶을 때는 문제가 생긴다
왜냐하면 생성자에서 인터페이스를 주입받고 있기 때문이다
이때 아주 손쉽게 해결을 할 수 있는데
public class EmailNotifier implements Notifier {
private Notifier notifier;
public EmailNotifier() {}
public EmailNotifier(Notifier notifier) {
this.notifier = notifier;
}
@Override
public void send() {
if (notifier != null) {
notifier.send();
}
System.out.println("이메일 알림");
}
}
이렇게 기본 생성자를 만들고 오버라이딩한 알림을 보내는 메소드에서
Notifier null 체크를 해서 손쉽게 이메일 알림만을 보낼 수 있게 되었다
장점
- 새로운 서브클래스를 만들지 않고 객체의 행동을 확장할 수 있다
- 런타임 시 기능을 추가하거나 제거할 수 있다
- 여러 데코레이터로 래핑하여 여러 행동을 합칠 수 있다
- 단일 책임 원칙 - 다양한 기능들의 다양한 변형들을 여러 개의 작은 클래스로 나눌 수 있다
단점
- 데코레이터 스택에서 특정 래퍼클래스를 제거하기 어렵다
- 순서에 의존하지 않게 데코레이터를 구현하기가 어렵다
- 계층들의 초기 설정 코드가 더러울 수 있다
'dev > 🧩 디자인패턴' 카테고리의 다른 글
컴포지트 패턴이란? (Composite Pattern) (0) | 2023.01.12 |
---|---|
상태 패턴이란? (State Pattern) (0) | 2022.12.31 |
프록시 패턴이란? (Proxy Pattern) (0) | 2022.12.18 |
템플릿 메소드 패턴이란? (Template Method Pattern) (0) | 2022.12.11 |
퍼사드 패턴이란? (Facade Pattern) (0) | 2022.11.24 |