
데코레이터 패턴(Decorator Pattern)은 객체에 동적으로 새로운 기능을 추가하는 디자인 패턴입니다. 상속을 사용하지 않고도 객체의 기능을 확장할 수 있으며, OCP(Open-Closed Principle, 개방-폐쇄 원칙) 을 준수하는 유용한 패턴입니다.
기능을 추가해야 하는 객체가 여러 종류일 때, 이를 해결하는 일반적인 방법은 상속(Inheritance) 입니다. 하지만 상속을 사용하면 클래스가 많아지고, 변경이 어려워지는 유연성 부족 문제가 발생할 수 있습니다.
데코레이터 패턴을 사용하면 런타임에 객체에 새로운 기능을 추가할 수 있으며, 기존 코드를 수정하지 않고도 확장이 가능합니다.

// Component 인터페이스 (기본 객체의 동작 정의)
public interface Beverage {
String getDescription();
double cost();
}
// 구체적인 Component (기본 음료)
public class Espresso implements Beverage {
@Override
public String getDescription() {
return "에스프레소";
}
@Override
public double cost() {
return 1.99;
}
}
// 데코레이터 추상 클래스
public abstract class CondimentDecorator implements Beverage {
protected Beverage beverage;
public CondimentDecorator(Beverage beverage) {
this.beverage = beverage;
}
@Override
public abstract String getDescription();
}
// 구체적인 데코레이터 (우유 추가)
public class Milk extends CondimentDecorator {
public Milk(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + ", 우유";
}
@Override
public double cost() {
return beverage.cost() + 0.50;
}
}
// 구체적인 데코레이터 (모카 추가)
public class Mocha extends CondimentDecorator {
public Mocha(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + ", 모카";
}
@Override
public double cost() {
return beverage.cost() + 0.70;
}
}
public class CoffeeShop {
public static void main(String[] args) {
Beverage espresso = new Espresso();
System.out.println(espresso.getDescription() + " $" + espresso.cost());
Beverage milkEspresso = new Milk(espresso);
System.out.println(milkEspresso.getDescription() + " $" + milkEspresso.cost());
Beverage mochaMilkEspresso = new Mocha(milkEspresso);
System.out.println(mochaMilkEspresso.getDescription() + " $" + mochaMilkEspresso.cost());
}
}
에스프레소 $1.99
에스프레소, 우유 $2.49
에스프레소, 우유, 모카 $3.19
Java의 java.io 패키지는 데코레이터 패턴을 활용한 대표적인 예입니다. InputStream을 기본 컴포넌트로 사용하고, BufferedInputStream, LineNumberInputStream 같은 데코레이터를 이용하여 기능을 동적으로 추가할 수 있습니다.
import java.io.*;
public class DecoratorPatternIOExample {
public static void main(String[] args) {
try {
InputStream inputStream = new FileInputStream("test.txt");
InputStream bufferedStream = new BufferedInputStream(inputStream);
InputStream lineNumberStream = new LineNumberInputStream(bufferedStream);
int data = lineNumberStream.read();
while (data != -1) {
System.out.print((char) data);
data = lineNumberStream.read();
}
lineNumberStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
위 코드에서 FileInputStream은 기본적인 파일 입력 기능을 제공하고, BufferedInputStream은 버퍼링 기능을 추가하며, LineNumberInputStream은 줄 번호를 추적할 수 있도록 기능을 확장하는 역할을 합니다. 이렇게 데코레이터 패턴을 활용하여 기존 클래스의 기능을 변경하지 않고 동적으로 확장할 수 있습니다.
데코레이터 패턴은 상속을 대체하여 동적으로 객체의 기능을 확장하는 강력한 방법입니다. 특히 GUI 컴포넌트, 스트림 처리(java.io 패키지), 텍스트 편집기 등 다양한 분야에서 널리 사용됩니다.