본문 바로가기
Design Pattern

[Design Pattern] 데코레이터 패턴

by 진꿈청 2024. 4. 1.

이번 포스팅 할 디자인 패턴은 데코레이터 패턴이다.

 

데코레이터는 말 그대로 장식하는 것이다.

 

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] 프록시 패턴  (1) 2024.03.12