13-6. 데코레이터 패턴 (Decorator Pattern)

박은서's avatar
Feb 24, 2026
13-6. 데코레이터 패턴 (Decorator Pattern)

1. 데코레이터 패턴 (Decorator Pattern)

1️⃣ 데코레이터 패턴이란?

1) 개념

객체에 동적으로 새로운 책임(기능)을 추가할 수 있도록 하는 구조 패턴

2) 핵심 아이디어

  • 상속 대신 객체 합성(composition) 사용
  • 기존 클래스 수정 없이 기능 확장
  • 객체를 "포장(wrapping)" 방식으로 기능 추가

2️⃣ 목적

데코레이터 패턴은 다음 문제 해결
  • 기능 조합이 많아질 때 상속 폭발 방지
  • 런타임에 기능 확장 가능
  • 기존 코드 변경 최소화 (OCP 준수)
➡️ "기능 확장 유연성 확보"

3️⃣ 구조

1) 주요 구성 요소

구성 요소
설명
Component
공통 인터페이스
ConcreteComponent
기본 객체
Decorator
Component 인터페이스 구현 + Component 참조
ConcreteDecorator
실제 기능 추가

2) 구조적 관계

Component (interface) ↑ ConcreteComponent Component (interface) ↑ DecoratorComponent 참조 ↑ ConcreteDecorator
  • 데코레이터는 Component 타입을 감싼다
  • 인터페이스가 동일 → 투명한 확장

4️⃣ 작동 원리 (How It Works)

  1. 기본 객체 생성
  1. 데코레이터로 감싼다
  1. 필요하면 여러 번 감싼다
객체 = 기능A(기능B(기본객체))
➡️ 기능을 레이어처럼 누적

5️⃣ 언제 사용하나? (When To Use)

  • 런타임에 기능 확장이 필요할 때
  • 상속이 비효율적일 때
  • 기능 조합이 많을 때
  • 클래스 수정 없이 확장해야 할 때

6️⃣ 데코레이터 패턴의 장단점

1) 장점

  • 상속 대체 가능
  • 기능 조합 유연성
  • 기존 코드 변경 없음 (OCP)
  • 책임 분리 가능
  • 단일 책임 원칙(SRP) 강화

2) 단점

  • 객체 수 증가
  • 디버깅 복잡도 증가
  • 구조 이해 어려움
  • 설정 코드가 장황해질 수 있음

7️⃣ 예제

1) ☕ 커피 주문 시스템

  • 기본 객체
    • SimpleCoffee → 가격: 3000
  • 데코레이터 적용
    • MilkDecorator → +500WhipDecorator → +700
  • 조합 결과
    • Whip(Milk(SimpleCoffee))3000 + 500 + 700
➡️ 기능을 자유롭게 조합 가능

8️⃣ 코드 예시

// Component interface Coffee { int cost(); } // ConcreteComponent class SimpleCoffee implements Coffee { public int cost() { return 3000; } } // Decorator abstract class CoffeeDecorator implements Coffee { protected Coffee coffee; CoffeeDecorator(Coffee coffee) { this.coffee = coffee; } } // ConcreteDecorator class MilkDecorator extends CoffeeDecorator { MilkDecorator(Coffee coffee) { super(coffee); } public int cost() { return coffee.cost() + 500; } }

2. 실습 (ex06)

1️⃣ 테스트용

1) BasicNotifier.java

C:\workspace\java_lab\designapp\src\ex06\notification\BasicNotifier.java
package ex06.notification; public class BasicNotifier { public void send() { System.out.println("기본 알림"); } }

2) App.java

C:\workspace\java_lab\designapp\src\ex06\App.java
package ex06; import ex06.notification.BasicNotifier; public class App { public static void main(String[] args) { BasicNotifier b1 = new BasicNotifier(); b1.send(); } }

3) 결과

notion image

4) EmailNotifier.java

C:\workspace\java_lab\designapp\src\ex06\notification\EmailNotifier.java
package ex06.notification; public class EmailNotifier { public void send() { System.out.println("이메일 알림"); } }

5) App.java

C:\workspace\java_lab\designapp\src\ex06\App.java
package ex06; import ex06.notification.BasicNotifier; import ex06.notification.EmailNotifier; public class App { public static void main(String[] args) { EmailNotifier b1 = new EmailNotifier(); BasicNotifier b2 = new BasicNotifier(); b1.send(); b2.send(); } }

6) 결과

notion image

2️⃣ 인터페이스 추가

메서드 실수하지 않게 인터페이스로 제약!

1) Notifier.java (인터페이스)

C:\workspace\java_lab\designapp\src\ex06\notification\Notifier.java
package ex06.notification; public interface Notifier { void send(); }

2) BasicNotifier.java

package ex06.notification; public class BasicNotifier implements Notifier { public void send() { System.out.println("기본 알림"); } }

3) EmailNotifier.java

package ex06.notification; public class EmailNotifier implements Notifier { public void send() { System.out.println("이메일 알림"); } }

4) SmsNotifier.java

C:\workspace\java_lab\designapp\src\ex06\notification\SmsNotifier.java
package ex06.notification; public class SmsNotifier implements Notifier{ @Override public void send() { System.out.println("문자 알림"); } }

3️⃣ 데코레이터 패턴

1) BasicNotifier.java

package ex06.notification; public class BasicNotifier implements Notifier { private Notifier notifier; // 뭐든지 has 할 수 있다(모든 것이 Notifier를 상속하고 있기 때문) public BasicNotifier() {} public BasicNotifier(Notifier notifier) { this.notifier = notifier; } public void send() { if (notifier != null) notifier.send(); System.out.println("기본 알림"); } }

2) App.java

package ex06; import ex06.notification.BasicNotifier; import ex06.notification.EmailNotifier; import ex06.notification.SmsNotifier; /** * { * "sends" : [basic, email], * "msg": "반가워!!" * } */ // 책임 : 기존에 있는 클래스에 기능을 확장하면서 계속 추가하는 패턴 public class App { public static void main(String[] args) { BasicNotifier b1 = new BasicNotifier(new SmsNotifier()); b1.send(); } }

3) 결과

notion image

4) EmailNotifier.java

package ex06.notification; public class EmailNotifier implements Notifier { private Notifier notifier; // 뭐든지 has 할 수 있다(모든 것이 Notifier를 상속하고 있기 때문) public EmailNotifier() {} public EmailNotifier(Notifier notifier) { this.notifier = notifier; } public void send() { if (notifier != null) notifier.send(); System.out.println("이메일 알림"); } }

5) SmsNotifier.java

package ex06.notification; public class SmsNotifier implements Notifier{ private Notifier notifier; // 뭐든지 has 할 수 있다(모든 것이 Notifier를 상속하고 있기 때문) public SmsNotifier() {} public SmsNotifier(Notifier notifier) { this.notifier = notifier; } public void send() { if (notifier != null) notifier.send(); System.out.println("문자 알림"); } }

4️⃣ EXAM

1) App.java

package ex06; import ex06.notification.BasicNotifier; import ex06.notification.EmailNotifier; import ex06.notification.SmsNotifier; /** * { * "sends" : [basic, email], * "msg": "반가워!!" * } */ // 책임 : 기존에 있는 클래스에 기능을 확장하면서 계속 추가하는 패턴 public class App { public static void main(String[] args) { // 1번 문제 (SMS 알림, SMS 알림, Email 알림) EmailNotifier b1 = new EmailNotifier(new SmsNotifier(new SmsNotifier())); b1.send(); System.out.println("-----------------------------------------"); // 2번 문제 (기본 알림, SMS 알림, Email 알림) EmailNotifier b2 = new EmailNotifier(new SmsNotifier(new BasicNotifier())); b2.send(); System.out.println("-----------------------------------------"); // 3번 문제 (SMS 알림) SmsNotifier b3 = new SmsNotifier(); b3.send(); } }

2) 결과

notion image
Share article