객체들을 새로운 행동들을 포함한 특수 래퍼 객체들 내에 넣어서 위 행동들을 해당 객체들에 연결시키는 구조적 디자인 패턴
1. 개요
- 객체의 기능을 동적으로 추가하는 구조적 패턴.
- 상속 대신 객체를 감싸서 기능을 확장.
- 기존 객체를 변경하지 않고 기능을 추가할 수 있음.
2. 구성 요소
- Component: 기능을 추가할 기본 인터페이스.
- ConcreteComponent: 기본 객체로, 실제 기능을 구현.
- Decorator: 기본 객체를 감싸는 추상 클래스.
- ConcreteDecorator: 기능 추가를 위한 구체적 클래스.
3. 장점
- 유연성: 런타임 중에 기능을 동적으로 추가.
- 재사용성: 다양한 객체에서 기능을 재사용 가능.
- 응집성: 기능별로 독립적인 클래스로 관리.
4. 단점
- 객체 수 증가: 데코레이터가 많으면 객체 수가 증가.
- 복잡성 증가: 데코레이터가 많을수록 조합을 관리하기 어려움.
5. Java 코드 예시
5.1. Coffee
인터페이스 (Component)
java
코드 복사
// Component: 기본 인터페이스
public interface Coffee {
double cost(); // 가격을 반환하는 메서드
}
5.2. SimpleCoffee
클래스 (ConcreteComponent)
java
코드 복사
// ConcreteComponent: 기본 커피 객체
public class SimpleCoffee implements Coffee {
@Override
public double cost() {
return 5.0; // 기본 커피 가격
}
}
5.3. CoffeeDecorator
클래스 (Decorator)
java
코드 복사
// Decorator: 기본 객체를 감싸는 추상 클래스
public abstract class CoffeeDecorator implements Coffee {
protected Coffee coffee; // 기본 커피 객체
// 생성자를 통해 기본 커피 객체를 설정
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
@Override
public double cost() {
return coffee.cost(); // 기본 커피 가격 반환
}
}
5.4. MilkDecorator
클래스 (ConcreteDecorator)
java
코드 복사
// ConcreteDecorator: 우유 추가
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee); // 기본 커피 객체 설정
}
@Override
public double cost() {
return super.cost() + 2.0; // 우유 추가 비용 2.0 더하기
}
}
5.5. SugarDecorator
클래스 (ConcreteDecorator)
java
코드 복사
// ConcreteDecorator: 설탕 추가
public class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee); // 기본 커피 객체 설정
}
@Override
public double cost() {
return super.cost() + 1.0; // 설탕 추가 비용 1.0 더하기
}
}
5.6. 테스트 클래스 (클라이언트 코드)
java
코드 복사
// 테스트 클래스: 데코레이터 패턴 사용 예시
public class DecoratorPatternTest {
public static void main(String[] args) {
Coffee simpleCoffee = new SimpleCoffee();
System.out.println("Simple Coffee cost: " + simpleCoffee.cost());
// 우유 추가된 커피
Coffee milkCoffee = new MilkDecorator(simpleCoffee);
System.out.println("Milk Coffee cost: " + milkCoffee.cost());
// 설탕과 우유가 추가된 커피
Coffee milkSugarCoffee = new SugarDecorator(milkCoffee);
System.out.println("Milk and Sugar Coffee cost: " + milkSugarCoffee.cost());
// 설탕, 우유, 기본 커피가 추가된 커피
Coffee fullyDecoratedCoffee = new SugarDecorator(new MilkDecorator(new SimpleCoffee()));
System.out.println("Fully decorated Coffee cost: " + fullyDecoratedCoffee.cost());
}
}
5.6. 동작 예시
Simple Coffee cost: 5.0
Milk Coffee cost: 7.0
Milk and Sugar Coffee cost: 8.0
Fully decorated Coffee cost: 8.0
6. 비교: 데코레이터 vs 상속
ㅤ | 데코레이터 | 상속 |
기능 추가 | 동적으로 기능 추가 | 서브클래스를 통해 기능 추가 |
유연성 | 기능을 런타임에 조합 가능 | 컴파일 타임에 고정된 기능 |
객체 수 | 객체 수 증가 | 클래스 수 증가 |
7. 결론
- 상속 대신 객체를 감싸서 기능을 동적으로 확장.
- 다양한 기능을 독립적으로 관리할 수 있어, 유연하고 확장성 있는 코드 작성 가능.
- 기존 객체를 수정하지 않고 기능을 추가하는 방식으로 응집력을 높이고, 변경에 강한 구조를 제공.
Share article