이번 포스팅 할 디자인 패턴은 데코레이터 패턴이다.
데코레이터는 말 그대로 장식하는 것이다.
A, B, C 클래스가 있을 때
A -> B(A) -> C(B(A)) 이런식으로 장식해나갈 수 있는 패턴 또는 구조를 만드는 것을 데코레이터 패턴이라고 한다.
데코레이터 패턴을 사용하면 상속으로 복잡하고 중복된 코드를 만드는 것이 아니라
전략 패턴과 연계되어 좀 더 활용도 높은 구조를 만들 수 있다.
코드로 한 번 알아보자.
코드 구성
의존성 역전 원칙을 위한 Notifier 인터페이스
Notifier
public interface Notifier {
void send();
}
BasicNotifier
// 래퍼가 없는 데코레이터
// 이 클래스가 있어야 마무리 됨
public class BasicNotifier implements Notifier {
// 이 친구는 뭔가를 의존하면 안됨.
@Override
public void send(){
System.out.println("기본 알림");
}
}
EmailNotifier
기본 알림과 이메일 알림을 둘다 보내고 싶을 때 데코레이트를 위해 EmailNotifier 클래스를 만들고 관련 생성자를 만든다.
public class EmailNotifier implements Notifier{
private Notifier notifier;
public EmailNotifier(Notifier notifier){
this.notifier = notifier;
}
@Override
public void send() {
if(notifier != null)
notifier.send();
System.out.println("이메일 알림");
}
}
SmsNotifier
기본 알림과 문자 알림을 둘다 보내고 싶을 때 데코레이트를 위해 EmailNotifier 클래스를 만들고 관련 생성자를 만든다.
public class SmsNotifier implements Notifier{
private Notifier notifier;
public SmsNotifier(Notifier notifier) {
this.notifier = notifier;
}
@Override
public void send() {
if(notifier != null)
notifier.send();
System.out.println("문자 알림");
}
}
이렇게 Notifier라는 인터페이스를 구현하는 형식으로 코드를 작성하면
우리는 SmsNotifier에 BasicNotifier도 넣을 수 있고 EmailNotifier도 넣을 수 있다.
즉, 데코레이터 패턴 사용으로 아주 광범위하게 활용이 가능해진다.
그 예시를 App에서 보자.
App
/**
* 데코레이터 패턴 (장식하다)
* (A) -> B(A) -> C(B(A))
*/
public class App {
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");
// 3. 기본 알림(기본알림 -> ~~~~)
Notifier allNotifier3 = new EmailNotifier(new SmsNotifier(new SmsNotifier(new BasicNotifier())));
allNotifier3.send();
System.out.println("__end");
// 4. 기본 알림
Notifier basicNotifier = new BasicNotifier();
basicNotifier.send();
System.out.println("__end");
// 5. 기본 알림 + 이메일 알림
Notifier emailNotifier = new EmailNotifier(new BasicNotifier());
emailNotifier.send();
System.out.println("__end");
// 6. 기본 알림 + 문자 알림
Notifier smsNotifier = new SmsNotifier(new BasicNotifier());
smsNotifier.send();
System.out.println("__end");
}
}
이런식으로 굉장히 많은 조합이 가능해진다.
3번의 경우처럼 무제한으로 작성이 가능해진다.
하지만, 여기서 문제점이 하나 있는데 EmailNotifier와 SmsNotifier은 기본 생성자가 없다.
그래서 단독으로 사용할 수 없으며 기본 생성자를 생성하여 사용해도 Notifier 객체를 전달받지 못해 Null 오류가 발생한다.
public class EmailNotifier implements Notifier{
private Notifier notifier;
public EmailNotifier() {
}
public EmailNotifier(Notifier notifier){
this.notifier = notifier;
}
@Override
public void send() {
notifier.send();
System.out.println("이메일 알림");
}
}
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "pattern.ex06.notification.Notifier.send()" because "this.notifier" is null
at pattern.ex06.notification.EmailNotifier.send(EmailNotifier.java:17)
at pattern.ex06.App.main(App.java:58)
따라서, 결국 BasicNotifier로 끝나야 한다.
하지만, 이건 아주 간단한 방법으로 해결이 가능하다.
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("이메일 알림");
}
}
널이 아닌 경우에만 출력하도록 하면 된다.
이렇게 기본생성자를 생성함으로 우리는 더 많은 조합 사용이 가능하다.
App
/**
* 데코레이터 패턴 (장식하다)
* (A) -> B(A) -> C(B(A))
*/
public class App {
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");
// 3. 기본 알림(기본알림 -> ~~~~)
Notifier allNotifier3 = new EmailNotifier(new SmsNotifier(new SmsNotifier(new BasicNotifier())));
allNotifier3.send();
System.out.println("__end");
// 4. 기본 알림
Notifier basicNotifier = new BasicNotifier();
basicNotifier.send();
System.out.println("__end");
// 5. 기본 알림 + 이메일 알림
Notifier emailNotifier = new EmailNotifier(new BasicNotifier());
emailNotifier.send();
System.out.println("__end");
// 6. 기본 알림 + 문자 알림
Notifier smsNotifier = new SmsNotifier(new BasicNotifier());
smsNotifier.send();;
System.out.println("__end");
// 7. 이메일 알림
Notifier onlyEmailNotifier = new EmailNotifier();
onlyEmailNotifier.send();
System.out.println("__end");
// 8. 문자 알림
Notifier onlySmsNotifier = new SmsNotifier();
onlySmsNotifier.send();
System.out.println("__end");
// 9. 문자알림 + 이메일 알림
Notifier smsAndEmailNotifier = new EmailNotifier(new SmsNotifier());
smsAndEmailNotifier.send();
System.out.println("__end");
}
}
이제는 이메일 알림과 문자 알림을 기본 생성자를 통한 단독 사용도 가능해졌다.
출력
기본 알림
이메일 알림
문자 알림
__end
기본 알림
문자 알림
이메일 알림
__end
기본 알림
문자 알림
문자 알림
이메일 알림
__end
기본 알림
__end
기본 알림
이메일 알림
__end
기본 알림
문자 알림
__end
정리
정리하자면,
데코레이터 패턴은 한 클래스가 다른 클래스를 장식할 수 있으며,
데코레이터 패턴을 사용하면 상속으로 복잡하고 중복된 코드를 만드는 것이 아니라
전략 패턴과 연계되어 좀 더 활용도 높은 구조를 만들 수 있다.
'Design Pattern' 카테고리의 다른 글
[Design Pattern] 팩토리 패턴 (0) | 2024.04.04 |
---|---|
[Design Pattern] 탬플릿 메서드 패턴 (0) | 2024.03.28 |
[Design Pattern] 싱글톤 패턴 (0) | 2024.03.25 |
[Design Pattern] 어댑터 패턴 (0) | 2024.03.18 |
[Design Pattern] 프록시 패턴 (0) | 2024.03.12 |