目的
動態地將額外權責附加於物件上,不必衍生子類別(透明地)即可彈性地擴增物件功能。
Transparently augment objects with new responsibilities dynamically.
適用
- 想擴充功能,但子類別繼承方式是不切實際時(功能太多,繼承數量龐大)。
- 在不影響其他物件的情況下,可以動態而透明的增加權責到個別物件上,也可以動態撤銷。
結構及成員
Collaborations: Decorator會將訊息轉傳給Component處理,且在轉傳之前或之後可以做額外的事。(簡單說就是可以在原本Component要做的事,利用Decorator多做其他事)
影響結果
好處
壞處
實作
遵循介面/介面一致性(conformance)
Decorator物件的介面必須和Component一係,所以每一種ConcreteDecorator類別都要繼承同一種Decorator。
(輕量化)Decorator抽象類別
如果只要增加一個權責,則Decorator的抽象就可不定義,直接將Decorator的訊息轉入ConcreteDecorator。
維持Component類別的輕巧
Component只設介面,不存資料。讓ConcreteComponent處理複雜的資料。
變臉 vs. 換骨
Decorator Pattern像是包在物件外面的一層皮,而 Strategy Pattern 像是改變物件的內臟。如果Component本身太過臃腫,可以改用Strategy Pattern,Strategy Pattern同樣可以連續串接Strategy Pattern。
Example: Beverage
//Class: Beverage
public abstract class Beverage {
String description = "Unknown Beverage"
public String getDescription() {
return description;
}
// 留給子類別實作
public abstract double cost();
}
//Class: CondimentDecorator
public abstract class CondimentDecorator extends Beverage {
// 因為希望配料也能顯示出來,
// 因此所有子類別都要實作這個 method
public abstract String getDescription();
}
//Class: Espresso
public class Espresso extends Beverage {
public Espresso {
// 這個變數是繼承自 Beverage 的
description = "Espresso";
}
public double cost() {
// 目前這邊只是單純 Espresso 的價格,
// 不含任何配料
return 1.99;
}
}
//Class: Mocha
// 摩卡是一個裝飾者, 因此讓它繼承自 CondimentDecorator
// 而且別忘了, CondimentDecorator 繼承自 Beverage 喔
public class Mocha extends CondimentDecorator {
// 要讓摩卡能參考到 Beverage,
// 因此需要這個成員變數
Beverage mBeverage;
public Mocha(Beverage beverage) {
this.mBeverage = beverage;
}
// 以下兩個 method 的作法都是利用委派(delegation) 的方式,
// 從被裝飾者拿到資訊後, 再加上裝飾者的資訊
public String getDescription() {
return mBeverage.getDescription() + ", Mocha";
}
public double cost() {
return .20 + mBeverage.cost();
}
}
//Class: Milk
// 牛奶裝飾者基本上寫法跟摩卡裝飾者一樣
public class Milk extends CondimentDecorator {
Beverage mBeverage;
public Milk(Beverage beverage)
{
this.mBeverage = beverage;
}
public String getDescription()
{
return mBeverage.getDescription() + ", Milk";
}
public double cost()
{
return .30 + mBeverage.cost();
}
}
//Class: BevrageTest
public class BeverageTest {
pubilc static void main(String[] args)
{
// 點了一杯 Espresso, 印出它的資訊
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription() +
" $" + beverage.cost());
// 再點一杯 Espresso
Beverage beverage2 = new Espresso();
// 用摩卡裝飾 Espresso
beverage2 = new Mocha(beverage2);
// 用牛奶裝飾加了摩卡的 Espresso
beverage2 = new Milk(beverage2);
System.out.println(beverage2.getDescription() +
" $" + beverage2.cost());
}
}
Result:
Espresso $1.99
Espresso, Mocha, Milk $2.49