mojo's Blog
Decorator Pattern 본문
Decorator Pattern
목적: 객체의 기존 책임과 동작을 수정하기 위해 객체를 동적으로 감쌀 수 있음
사용시기
- 객체 책임과 행동은 동적으로 수정할 수 있어야 함
- 구체적인 구현은 책임과 행동으로부터 분리되어야 함
- 수정을 달성하기 위한 하위 분류는 비현실적이거나 불가능함
- 특정 기능은 객체 계층에서 상위에 위치해서는 안됨
- 구체적인 구현을 둘러싼 많은 작은 객체가 허용
※ Design Principle: OCP
OCP
- 클래스는 확장을 위해 열려 있어야 하지만 수정을 위해 닫혀 있어야 함
- 기존 코드를 수정하지 않고 새로운 동작을 통합하기 위해 클래스를 쉽게 확장할 수 있어야 함
- 변화에 탄력적이며 변화하는 요구 사항을 충족하기 위해 새로운 기능을 담당할 수 있을 정도로 유연함
주의할 점: 모든 경우에 OCP 적용을 하지 말고 가능하면 간단한 디자인을 유지할 것
※ The Definition of the Decorator
객체에 동적으로 추가적인 책임을 부여하는 것이 Decorator 패턴이다.
Decorator는 기능 확장을 위해 하위 분류에 대한 유연한 대안을 제공한다.
※ Example
서브웨이를 Decorator pattern에 적용해보도록 한다.
서브웨이 메뉴는 Veggie, Turkey, RoastBeef 으로 정했으며,
이를 장식할 소스는 SaltPepper, Mustard, OliveOil 으로 정했다.
이를 ConcreteComponent 및 Decorator 로 분류하면 아래와 같다.
- Concrete Component: Veggie, Turkey, RoastBeef
- Decorator: SaltPepper, Mustard, OliveOil
이를 클래스 다이어그램으로 나타내면 아래와 같다.
※ Subway class & Concrete Subway class
public abstract class Subway {
protected String description = “Unknown Subway”;
public String getDescription() {
return description;
}
public abstract double cost();
}
public class Veggie extends Subway {
public Veggie() {
description = “Veggie”;
}
public double cost() {
return 0.39;
}
}
Subway 는 추상 클래스로 메뉴 별로 description 및 cost 가 다르기 때문에,
Concrete Subway 에서 Subway 를 상속함으로써 description 및 cost 를 메뉴에 맞게끔 코딩한다.
※ Coding Sauce
public abstract class SauceDecorator extends Subway {
protected Subway subway;
public abstract String getDescription();
}
public class SaltPepper extends SauceDecorator{
public SaltPepper(Subway subway) {
this.subway = subway;
}
public String getDescription(){
return subway.getDescription() + “, SaltPepper”;
}
public double cost() {
return 0.05 + subway.cost();
}
}
Subway를 상속하는 SauceDecorator 는 추상 클래스로 생성한다.
그리고 각 소스별로 SauceDecorator 클래스를 상속한다.
생성자에서는 Subway 객체를 감싸서 메뉴에 적용할 소스를 추가시킬 수 있다.
※ Decorator Test Drive
public class MySubway {
public static void main(String args[]) {
Subway subway = new Veggie();
System.out.println(subway.getDescription()
+ “ $” + subway.cost());
Subway subway2 = new Turkey();
subway2 = new SaltPepper(subway2);
subway2 = new OlvieOil(subway2);
System.out.println(subway2.getDescription()
+ “ $” + subway2.cost());
}
}
- subway
메뉴는 베지, 소스는 없이 만든 객체이다.
- subway2
메뉴는 터키, 소스는 소금후추와 올리브오일로 만든 객체이다.
터키 가격 0.51, 소금후추 가격 0.05, 올리브오일 가격 0.1 으로 했을 때,
cost() 메서드가 호출되는 과정을 그림으로 나타내면 아래와 같다.
1. OliveOil 객체의 cost() 가 호출되면서 상위 Subway 객체의 cost() 를 호출하게 된다.
0.1 + subway.cost() // subway: SaltPepper
2. SaltPepper 객체의 cost() 가 호출되면서 상위 Subway 객체의 cost() 를 호출하게 된다.
0.1 + 0.05 + subway.cost() // subway: Turkey
3. Turkey 객체의 cost() 는 메뉴 가격만 반환하므로 값을 반환하게 된다.
0.1 + 0.05 + 0.51
4. 최종적으로 반환되어지는 값들을 다 합하여 반환되면 0.66 이 나오게 된다.
※ Decorator Pattern
- 디자인 원칙은 OCP(Open-Closed Principle)
- 장점
객체에 동적으로 추가 책임을 부여
기능 확장을 위한 하위 분류에 대한 유연한 대안 제공
- 단점
많이 작은 클래스를 생성할 수 있음
패턴을 모르면 이해하기 어려움
- 주요 메커니즘
object composition + delegation 을 사용함
데코레이터 클래스는 장식하고 있는 컴포넌트의 유형을 미러링함
(많은 수의 데코레이터로 컴포넌트를 포장할 수 있음)
'Design Patterns' 카테고리의 다른 글
Composite Pattern (3) | 2024.06.09 |
---|---|
Adapter Pattern (0) | 2024.06.09 |
Singleton Pattern (1) | 2024.06.09 |
Builder Pattern (0) | 2024.06.09 |
Factory Method Pattern & Abstract Factory Pattern (0) | 2024.06.09 |