1. 데코레이터 패턴 (Decorator Pattern)
1️⃣ 데코레이터 패턴이란?
1) 개념
객체에 동적으로 새로운 책임(기능)을 추가할 수 있도록 하는 구조 패턴
2) 핵심 아이디어
- 상속 대신 객체 합성(composition) 사용
- 기존 클래스 수정 없이 기능 확장
- 객체를 "포장(wrapping)" 방식으로 기능 추가
2️⃣ 목적
데코레이터 패턴은 다음 문제 해결
- 기능 조합이 많아질 때 상속 폭발 방지
- 런타임에 기능 확장 가능
- 기존 코드 변경 최소화 (OCP 준수)
➡️ "기능 확장 유연성 확보"
3️⃣ 구조
1) 주요 구성 요소
구성 요소 | 설명 |
Component | 공통 인터페이스 |
ConcreteComponent | 기본 객체 |
Decorator | Component 인터페이스 구현 + Component 참조 |
ConcreteDecorator | 실제 기능 추가 |
2) 구조적 관계
Component (interface)
↑
ConcreteComponent
Component (interface)
↑
Decorator → Component 참조
↑
ConcreteDecorator- 데코레이터는 Component 타입을 감싼다
- 인터페이스가 동일 → 투명한 확장
4️⃣ 작동 원리 (How It Works)
- 기본 객체 생성
- 데코레이터로 감싼다
- 필요하면 여러 번 감싼다
객체 = 기능A(기능B(기본객체))➡️ 기능을 레이어처럼 누적
5️⃣ 언제 사용하나? (When To Use)
- 런타임에 기능 확장이 필요할 때
- 상속이 비효율적일 때
- 기능 조합이 많을 때
- 클래스 수정 없이 확장해야 할 때
6️⃣ 데코레이터 패턴의 장단점
1) 장점
- 상속 대체 가능
- 기능 조합 유연성
- 기존 코드 변경 없음 (OCP)
- 책임 분리 가능
- 단일 책임 원칙(SRP) 강화
2) 단점
- 객체 수 증가
- 디버깅 복잡도 증가
- 구조 이해 어려움
- 설정 코드가 장황해질 수 있음
7️⃣ 예제
1) ☕ 커피 주문 시스템
- 기본 객체
SimpleCoffee → 가격: 3000원- 데코레이터 적용
MilkDecorator → +500원
WhipDecorator → +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) 결과

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) 결과

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) 결과

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) 결과

Share article