Decorator Pattern
왜 데코레이터 패턴이 필요할까?
소프트웨어를 개발하다 보면, 객체에 새로운 기능을 추가하거나 변경해야 하는 경우가 많습니다.
하지만 기존 클래스의 코드를 수정하거나 새로운 서브클래스를 만들어 기능을 확장하면 다음과 같은 문제가 생깁니다:
- 기존 코드 수정 위험 : 기존 클래스 수정은 오류를 유발할 수 있습니다.
- 서브클래스 폭발 문제 : 모든 조합을 처리하려면 서브클래스가 기하급수적으로 늘어납니다.
해결책
데코레이터 패턴은 객체를 감싸는 방식으로 기능을 동적으로 확장할 수 있습니다.
이를 통해 코드 수정 없이도 새로운 기능을 추가하고, 서브클래스 증가를 방지할 수 있습니다.
데코레이터 패턴의 구조 : 트리 비유로 이해하기
데코레이터 패턴은 기본 객체를 꾸미는 과정을 비유적으로 설명할 수 있습니다.

크리스마스 트리 예시
- 기본 트리: 아무 장식이 없는 단순한 트리 (기본 객체)
- 장식: 전구, 리본, 별 등을 추가하여 꾸밈 (새로운 기능 추가)
- 조합 가능성: 전구와 리본을 동시에 추가하거나, 별만 추가하는 등 조합의 유연성

데코레이터 패턴의 핵심 구조
1. Component(기본 컴포넌트)
확장하거나 장식할 객체의 공통 인터페이스입니다. 기본적은 트리 형태와 동작을 정의합니다.
2. Concrete Component(구체적 컴포넌트)
실제로 기본 트리 모양을 가진 객체입니다. 이 객체가 데코레이터에 의해 꾸며집니다.
3. Decorator(데코레이터 추상 클래스)
Component 인터페이스를 구현하며, 기존 트리를 감싸는 역할을 합니다. 데코레이터는 기본 트리의 기능을 호출한 뒤, 새로운 장식을 추가합니다.
4. Concrete Decorator(구체적 데코레이터)
실제로 새로운 장식을 추가하거나 동작을 확장하는 클래스입니다.
크리스마스 트리 예시 코드

Component: 트리 인터페이스
interface ChristmasTree {
String decorate();
}
Concrete Component: 기본 트리
class SimpleChristmasTree implements ChristmasTree {
@Override
public String decorate() {
return "크리스마스 트리";
}
}
Decorator: 추상 클래스
abstract class TreeDecorator implements ChristmasTree {
protected ChristmasTree tree;
public TreeDecorator(ChristmasTree tree) {
this.tree = tree;
}
@Override
public String decorate() {
return tree.decorate();
}
}
Concrete Decorators: 장식 추가
class LightsDecorator extends TreeDecorator {
public LightsDecorator(ChristmasTree tree) {
super(tree);
}
@Override
public String decorate() {
return super.decorate() + " + 반짝이는 전구";
}
}
class GarlandDecorator extends TreeDecorator {
public GarlandDecorator(ChristmasTree tree) {
super(tree);
}
@Override
public String decorate() {
return super.decorate() + " + 화려한 리본";
}
}
class StarDecorator extends TreeDecorator {
public StarDecorator(ChristmasTree tree) {
super(tree);
}
@Override
public String decorate() {
return super.decorate() + " + 황금 별";
}
}
Main
public class DecoratorPatternMain {
public static void main(String[] args) {
ChristmasTree tree = new SimpleChristmasTree();
System.out.println(tree.decorate()); // 기본 트리
tree = new LightsDecorator(tree);
System.out.println(tree.decorate()); // 전구 추가
tree = new GarlandDecorator(tree);
System.out.println(tree.decorate()); // 리본 추가
tree = new StarDecorator(tree);
System.out.println(tree.decorate()); // 별 추가
}
}
데코레이터 패턴의 장점과 한계
장점
1. 동적 확장
런타임에 객체에 새로운 기능 추가 가능
2. 유연한 조합
필요한 기능만 조합하여 구현
3. OCP(Open-Closed Principle) 준수
기존 코드를 변경하지 않고 기능 확장 가능
한계
1. 복잡성 증가
데코레이터를 다수 사용하면 구조가 복잡해질 수 있음
2. 객체 래핑 비용
객체를 감싸는 방식은 성능 비용이 발생할 수 있음
자바 I/O 라이브러리
Java의 BufferedReader와 InputStreamReader는 데코레이터 패턴의 대표적인 예입니다.
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
기본 객체: InputStreamReader
데코레이터: BufferedReader (버퍼링 기능 추가)
데코레이터 패턴을 언제 사용할까?
객체의 기능 확장이 빈번하고, 유연한 조합이 필요할 때 적합합니다.
서브클래싱 없이도 새로운 기능을 추가할 수 있는 데코레이터 패턴은 유지보수성과 확장성을 높이는 데 중요한 도구가 될 수 있습니다.
'JAVA' 카테고리의 다른 글
팩토리 메서드 패턴 (0) | 2025.02.17 |
---|---|
옵저버 패턴 이해하기 (1) | 2025.01.20 |
전략 패턴(Strategy Pattern) (0) | 2025.01.12 |