1. 데코레이터 패턴이란?
구조 패턴 중 하나
객체에 추가 요소를 동적으로 더할 수 있다
서브클래스를 만들 때보다 더 유연하게 기능을 확장할 수 있다
2. 패턴 적용 전 코드
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--
하지만 이메일 알림과 기본 알림을 동시에 해야하는 요구사항이 생겼을 때
어떻게 처리를 하는 것이 좋을까?
여기서 프록시 패턴을 생각할 수도 있지만 데코레이터 패턴을 적용할 것이다
3. 패턴 적용 후
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--
이렇게 요구사항에 맞게 알림을 다양하게 줄 수 있게 되었다
4. 아까 전에 프록시 대신 데코레이터를 선택한 이유는?
프록시 패턴은 접근에 대해 제어를 목적으로 하고
데코레이터 패턴은 기존 기능에 다른 기능을 추가하는 목적이기 때문이다
5. 프록시 패턴과 공통점과 차이점
공통점
- 둘 다 interface 를 구현
차이점
- 프록시 패턴은 패턴이 적용된 클래스들의 관계가 컴파일 시에 정해짐
- 데코레이터 패턴은 런타임 시에 정해짐
6. 데코레이터 패턴을 적용했지만 해결하지 못한 문제점
이메일 알림과, 문자 알림 이 두 알림 중에 하나만 사용하고 싶을 때는 문제가 생긴다
왜냐하면 생성자에서 인터페이스를 주입받고 있기 때문이다
이때 아주 손쉽게 해결을 할 수 있는데
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 체크를 해서 손쉽게 이메일 알림만을 보낼 수 있게 되었다
7. 장점
- 새로운 서브클래스를 만들지 않고 객체의 행동을 확장할 수 있다
- 런타임 시 기능을 추가하거나 제거할 수 있다
- 여러 데코레이터로 래핑하여 여러 행동을 합칠 수 있다
- 단일 책임 원칙 - 다양한 기능들의 다양한 변형들을 여러 개의 작은 클래스로 나눌 수 있다
8. 단점
- 데코레이터 스택에서 특정 래퍼클래스를 제거하기 어렵다
- 순서에 의존하지 않게 데코레이터를 구현하기가 어렵다
- 계층들의 초기 설정 코드가 더러울 수 있다
'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 |