
데코레이터 패턴(Decorator Pattern)은 객체의 기능을 동적으로 확장할 수 있도록 설계된 구조적 디자인 패턴이다.
객체에 추가적인 책임을 부여하는 데 있어서 상속보다 유연한 대안을 제공한다.
데코레이터 패턴은 객체를 감싸는(decorate) 여러 개의 데코레이터 객체를 통해 기능을 추가하거나 변경할 수 있다
| 설명 | |
|---|---|
| 문제 | StarBuzz 커피숍에서, 고객은 음료를 주문할 수 있으며, 각종 옵션을 추가할 수 있어야 한다. |
| 해결책 | 기본 음료를 컴포넌트로 정의하고, 옵션을 데코레이터로 정의하여 음료 객체를 동적으로 꾸며준다. |
📌 상속을 이용하여 구현하였을때의 문제점

OCP(Open-Closed Principle) : 확장에는 열려 있고, 수정에는 닫혀 있는 코드

📌 상속 구조, 전체와 부분
Component : 요소의 공통 인터페이스나 추상 클래스ConcreteComponent : 기본적인 객체로, 데코레이터에 의해 감싸질 대상Decorator : 컴포넌트 인터페이스를 구현하거나 상속하는 추상클래스ConcreteDecorator : 데코레이터의 구체적인 구현으로, 기본 객체나 다른 데코레이터를 감싸서 추가적인 기능을 제공한다.🔔 데코레이터는 부모클래스(컴포넌트)의 매개변수(객체)를 생성자에서 받아서 꾸며준다!!

ConcreteDecorator 들은 Beverage에 대한 정보를 알고 있어야 함Milk또는 Mocha의 생성자에서 beverage를 매개변수로 받아서 초기화 시켜주는 코드 有public Mocha(Beverage beverage) {
this.beverage = beverage;
}Beverage b1 = new Milk(); → Milk 가 Decorator 라서 불가
Beverage b1 = new Espresso();
b1 = new Milk(b1); → 가능
💡 데코레이터 패턴에서 실제 호출은 가장 안쪽에 있는 기본 객체부터 시작한다. 데코레이터 객체들은 내부에 있는 객체(또는 또 다른 데코레이터)를 호출하고, 그 결과에 자신의 기능을 추가함
public class StarBuzz {
public static void main(String[] args) {
Beverage b1 = new HouseBlend();
System.out.println(b1.getDescription() + " $" + b1.cost());
b1 = new Milk(b1);
System.out.println(b1.getDescription() + " $" + b1.cost());
b1 = new Mocha(b1);
System.out.println(b1.getDescription() + " $" + b1.cost());
b1 = new Mocha(b1);
System.out.println(b1.getDescription() + " $" + b1.cost());
}
}
House Blend Coffee $0.89
House Blend Coffee, Milk $0.99
House Blend Coffee, Milk, Mocha $1.19
House Blend Coffee, Milk, Mocha, Mocha $1.39
💡 모든 객체가 Beverage 인터페이스나 추상 클래스를 구현하거나 상속받기 때문에, b1을 통해 getDescription()과 cost() 메서드를 호출할 수 있음!
💡 Beverage를 꾸며주는 요소(데코레이터) 들이 동일한 인터페이스를 구현하거나 동일한 추상 클래스를 상속받고 있기 때문에, 데코레이터와 실제 객체를 같은 타입으로 사용할 수 있게 된다!!
java.io 클래스는 데코레이터 패턴을 적용한 대표적인 라이브러리 이다!

InputStream내 FileInputStream을 보면, FileInputStream의 생성자에는 File 과 String 타입의 매개변수만 받음. InputStream과 관련된 항목이 없다,InputStream를 받는 매개변수가 없으므로, FileInputStream은 Concrete Component 이다!
InputStream내 FilterInputStream을 보면, FilterInputStream의 생성자에는 InputStream 타입 매개변수(컴포넌트) 를 받는 것을 확인할 수 있으므로, Decorator에 해당한다!
평소 입력에 자주 사용되는
BufferedReader도 데코레이터 패턴을 사용한 대표적인 예시이다!
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)));

🚩 Component : Reader 클래스. 문자 스트림을 읽기 위한 공통 메서드를 정의
🚩 ConcreteComponent : InputStreamReader 클래스. 바이트 스트림을 문자 스트림으로 변환. Reader의 구체적인 구현체
🚩 ConcreteDecorator : BufferedReader 클래스. 추가적인 버퍼링 기능을 제공하여 읽기 성능 향상
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
// System.in은 InputStream으로, 바이트 스트림을 제공
InputStream inputStream = System.in;
// InputStreamReader는 바이트 스트림을 문자 스트림으로 변환
InputStreamReader inputStreamReader = new InputStreamReader(inputStream); // ConcreteComponent
// BufferedReader는 문자 스트림에 버퍼링 기능을 추가
BufferedReader bufferedReader = new BufferedReader(inputStreamReader); //ConcreteDecorator
// 사용 예시: 한 줄을 입력 받아 출력
System.out.println("Enter a line:");
String line = bufferedReader.readLine();
System.out.println("You entered: " + line);
}
}