
클래스나 객체를 조합하여 더 큰 구조를 만드는 패턴
즉, 객체 간의 관계를 실현할 수 있는 간단한 방법으로써 설계를 용이하게 하는 패턴
구조 클래스 패턴은 상속을 통해 클래스나 인터페이스를 합성하고, 구조 객체 패턴은 객체를 합성하는 방법을 정의
객체를 동적으로 새로운 책임을 추가할 수 있게 하는 패턴
서브 클래스를 생성하는 것보다 더 유연한 방법 제공
객체를 데코레이터 클래스의 객체로 감싸 런타임에 객체의 기능을 확장하거나 변경하는 데 사용

기본 Coffee에 대한 인터페이스와 기본 구현체(BasicCoffee) 생성
public interface Coffee {
String getDescription();
int getCost();
}
public class BasicCoffee implements Coffee {
@Override
public String getDescription() {
return "Basic Coffee";
}
@Override
public int getCost() {
return 3000;
}
}
추상 클래스 CoffeeDecorator와 구체화하는 MilkDecorator와 WhipDecorator 생성
public abstract class CoffeeDecorator implements Coffee{
protected Coffee coffee;
//구성 요소의 레퍼런스를 포함한 인스턴스 변수가 존재
public CoffeeDecorator(Coffee coffee){ this.coffee = coffee; }
@Override
public String getDescription() {
return coffee.getDescription();
}
@Override
public int getCost() {
return coffee.getCost();
}
}
public class MilkDecorator extends CoffeeDecorator{
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription() + ", Milk";
}
@Override
public int getCost() {
return coffee.getCost() + 500;
}
}
public class WhipDecorator extends CoffeeDecorator{
public WhipDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription() + ", Whip";
}
@Override
public int getCost() {
return coffee.getCost() + 700;
}
}
런타임에 BasicCoffee 객체에서 Milk,Whip 데코레이터를 통해 객체를 감싸며 동적으로 기능을 확장
public class CoffeeMain {
public static void main(String[] args) {
Coffee coffee = new BasicCoffee();
System.out.println(coffee.getDescription() + " : " + coffee.getCost());
coffee = new MilkDecorator(coffee);
System.out.println(coffee.getDescription() + " : " + coffee.getCost());
coffee = new WhipDecorator(coffee);
System.out.println(coffee.getDescription() + " : " + coffee.getCost());
}
}
/*
Basic Coffee : 3000
Basic Coffee, Milk : 3500
Basic Coffee, Milk, Whip : 4200
*/
런타임에 기능 추가 가능
상속이 아닌 합성을 사용하여, 객체를 감싸며 기능을 점진적 추가
OCP 준수
기존 코드를 변경하지 않고, 새로운 클래스를 통해 기능 확장
상속보다 유연
상속은 컴파일 타임에 결정되지만, Decorator는 런타임에 조합 가능
재귀적 구조
Decorator는 같은 타입을 구현하므로, 자기 자신을 계속 감쌀 수 있는 구조를 가짐
new WhipDecorator(new MilkDecorator(new BasicCoffee)))
클라이언트는 Decorator와 원본 구분 불가능
복잡도 증가 가능
Decorator를 여러 개 중첩하는 경우 구조가 복잡해지고 디버깅이 어려워질 수 있음
Decorator가 새로운 메서드를 추가할 수도 있지만, 일반적으로 감싸는 객체에 있던 메소드를 별도의 작업으로 처리해서 새로운 기능을 추가
실제 기능을 수행하는 객체 대신 가상의 객체를 만들어 사용하는 패턴
어떤 객체를 사용하고자 할 때, 해당 객체를 직접 참조하는 것이 아니라, 그 객체를 대신(Proxy)하는 객체를 통해 접근하는 방식
해당 객체가 메모리상에 존재하지 않더라도 기본적으로 정보를 참조하거나 설정할 수 있고, 실제 객체의 기능이 반드시 필요한 시점까지 객체 생성을 미룰 수 있는 장점 존재

File 인터페이스와 구현체 NormalFile 생성
public interface File {
void read(String fileName);
}
public class NormalFile implements File{
@Override
public void read(String fileName) {
System.out.println("Reading.. " + fileName);
}
}
직접 NormalFile를 참조하는 대신 가상의 객체(NormalFileProxy)를 통해 해당 파일에 접근이 가능한지를 확인
public class NormalFileProxy implements File{
private NormalFile normalFile = new NormalFile();
private String password = "";
public NormalFileProxy(String password){
this.password = password;
}
@Override
public void read(String fileName) {
if (password.equals("security"))
normalFile.read(fileName);
else
System.out.println("Security File");
}
}
NormalFileProxy를 통해 접근 권한 판단 후 만약 접근 가능하다면 간접적으로 NormalFile에 참조
public class FileMain {
public static void main(String[] args) {
File securedFile = new NormalFileProxy("security");
securedFile.read("readme.md");
File normalFile = new NormalFileProxy("");
normalFile.read("readme.md");
}
}
/*
Security File
Reading.. readme.md
*/
https://jslee-tech.tistory.com/26
https://www.hanbit.co.kr/channel/view.html?cmscode=CMS8616098823