Menu
Coddy logo textTech

데코레이터 패턴

Coddy Java 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 87개 중 72번째.

Decorator 패턴은 객체를 decorator라고 하는 특수한 객체로 감싸 객체에 새로운 동작을 추가할 수 있게 해 주는 구조적 디자인 패턴입니다. 컴파일 시점에 동작을 추가하는 상속과 달리, decorator는 원래 class를 수정하지 않고 런타임에 동적으로 기능을 추가합니다.

기본 coffee로 시작하고 우유, 설탕 또는 휘핑크림과 같은 추가 항목을 넣을 수 있는 커피숍을 상상해 보세요. 각 추가 항목은 원래의 coffee를 감싸면서 비용과 설명을 더합니다. 이 패턴은 공통 인터페이스를 사용하므로 decorated 객체를 원래 객체와 동일하게 다룰 수 있습니다:

interface Coffee {
    String getDescription();
    double getCost();
}

class SimpleCoffee implements Coffee {
    public String getDescription() {
        return "Simple Coffee";
    }
    public double getCost() {
        return 2.0;
    }
}

abstract class CoffeeDecorator implements Coffee {
    protected Coffee coffee;
    
    public CoffeeDecorator(Coffee coffee) {
        this.coffee = coffee;
    }
}

class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }
    
    public String getDescription() {
        return coffee.getDescription() + ", Milk";
    }
    
    public double getCost() {
        return coffee.getCost() + 0.5;
    }
}

decorator는 래핑된 객체에 대한 참조를 보유하고, 자체 동작을 추가하면서 해당 객체에 호출을 위임합니다. 여러 decorator를 쌓아 기능을 결합할 수 있습니다:

Coffee order = new SimpleCoffee();
order = new MilkDecorator(order);
order = new SugarDecorator(order);

System.out.println(order.getDescription());  // 기본 커피, 우유, 설탕
System.out.println(order.getCost());         // 2.75

Decorator 패턴은 서브클래스를 폭발적으로 늘리지 않고 객체에 책임을 추가해야 할 때 빛을 발합니다. 각 decorator는 독립적이므로 자유롭게 조합하여 필요한 동작을 정확히 구성할 수 있습니다.

challenge icon

챌린지

쉬움

Decorator Pattern을 사용하여 알림 시스템을 만들어 봅시다! 서로 다른 전송 채널, 이메일 알림, SMS 알림, 푸시 알림으로 확장할 수 있는 기본 알림을 만들고, 데코레이터를 서로 감싸 구성합니다. 이는 하나의 메시지를 여러 채널을 통해 동시에 전송해야 할 수 있는 실제 시스템을 반영합니다.

코드를 네 개의 파일로 구성합니다.

  • Notifier.java: Decorator Pattern의 component interface 역할을 하는 Notifier interface를 Define합니다. 알림 전송을 처리하는 send(String message) 메서드를 Declare해야 합니다.
  • BasicNotifier.java: Notifier를 implements하는 concrete component class를 Create합니다. 이는 가장 간단한 형태의 알림인 인앱 알림을 나타냅니다. send가 호출되면 In-App: [message]를 출력해야 합니다.
  • NotifierDecorators.java: 여기에서 decorator hierarchy를 만듭니다. 먼저 Notifier를 implements하고, 래핑된 Notifier 객체에 대한 reference를 보유하는 abstract NotifierDecorator class로 시작합니다(이 객체는 constructor를 통해 전달됩니다).

    그런 다음 NotifierDecorator를 확장하는 세 개의 concrete decorator를 만듭니다.

    EmailDecorator - send가 호출되면 먼저 래핑된 notifier에 위임한 다음 Email: [message]를 출력합니다.

    SMSDecorator - send가 호출되면 먼저 래핑된 notifier에 위임한 다음 SMS: [message]를 출력합니다.

    PushDecorator - send가 호출되면 먼저 래핑된 notifier에 위임한 다음 Push: [message]를 출력합니다.

  • Main.java: decorator system을 하나로 결합합니다! 두 가지 입력을 받습니다. 알림 메시지(String)와 추가할 채널의 comma-separated 목록(String>)입니다. 예를 들면 <code>"email,sms" 또는 "push,email,sms"입니다.

    BasicNotifier로 시작합니다. 그런 다음 channels input에 따라 표시된 순서대로 적절한 decorator로 감쌉니다. 유효한 channel 이름은 email, sms, push입니다.

    마지막으로 fully decorated notifier에서 메시지를 사용하여 send를 호출합니다.

두 가지 입력을 순서대로 받습니다. 전송할 메시지(String)와 활성화할 channels(String, comma-separated)입니다.

예를 들어 입력이 Server is down!email,push인 경우 출력은 다음과 같습니다.

In-App: Server is down!
Email: Server is down!
Push: Server is down!

각 decorator가 원래 알림을 유지하면서 자체 동작을 추가하는 방식을 확인하세요. wrapping 순서가 출력 순서를 결정합니다. 가장 안쪽 component(BasicNotifier)가 먼저 실행되고, 그다음 각 decorator가 자신의 layer를 추가합니다. 각 조합에 대해 별도의 class를 만들지 않고도 원하는 channels 조합을 자유롭게 구성할 수 있습니다!

직접 해보기

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // 전송할 메시지 읽기
        String message = scanner.nextLine();
        
        // 쉼표로 구분된 채널 목록 읽기 (예: "email,sms" 또는 "push,email,sms")
        String channelsInput = scanner.nextLine();
        
        // TODO: BasicNotifier로 시작하기
        
        // TODO: 쉼표를 기준으로 채널 입력 분할하기
        
        // TODO: 각 채널을 순회하며 적절한 데코레이터로 notifier 감싸기
        // 유효한 채널: "email", "sms", "push"
        
        // TODO: 메시지와 함께 완전히 데코레이트된 notifier에서 send 호출하기
        
    }
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 Java 컴파일러