定義
能夠動態的給物件增加行為職責的一種模式,靈活性遠勝於繼承。
主要組成
抽象元件(Component): 定義抽象行為介面。
具體元件(Concrete component): 定義具體實現行為介面的類,繼承自抽象元件,也做被裝飾者,用於被附加各種行為。
抽象裝飾者(Decorator):持有一個Component的引用,並繼承自Component,提供其一致的介面。這邊繼承自Component來達到型別匹配的效果,而不是為了利用繼承來獲取行為,行為來自裝飾者與基礎元件的組合關係。
具體裝飾者(Concrete Decorator):負責給Component元件物件新增具體的責任,繼承自Decorator
UML類圖
框架程式碼
抽象元件(Component):
public interface Component {
void methodA();
void methodB();
}
複製程式碼
具體元件(Concrete component):
public class ConcreteComponent implements Component{
@Override
public void methodA() {
//...
}
@Override
public void methodB() {
//...
}
}
複製程式碼
抽象裝飾者(Decorator):
public abstract class Decorator implements Component{
Component component;
public Decorator(Component component) {
this.component = component;
}
}
複製程式碼
具體裝飾者(Concrete Decorator):
public class ConcreteDecoratorA extends Decorator{
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void methodA() {
component.methodA();
//...裝飾者擴充套件Component狀態
}
@Override
public void methodB() {
component.methodB();
//...裝飾者擴充套件Component狀態
}
}
public class ConcreteDecoratorB extends Decorator{
public ConcreteDecoratorB(Component component) {
super(component);
}
@Override
public void methodA() {
//...
}
@Override
public void methodB() {
//...
}
}
複製程式碼
具體例子
以銷售咖啡為例,為咖啡店設計實現飲料售價的類結構,假設存在:
咖啡種類:美式咖啡(American coffee)、英式咖啡(English coffee)、拿鐵(Latte),售價分別為每杯15,16,17元。
配料種類:蒸奶(Milk)、豆漿(Soy)、摩卡(Mocha),售價為每份3,1,2元。
UML類圖
程式碼
Beverage(對應抽象元件Component):
public interface Beverage {
float cost();
}
複製程式碼
Coffee(對應具體元件Concrete component):
美式咖啡:
public class AmericanCoffee implements Beverage{
@Override
public float cost() {
return 15;
}
}
複製程式碼
英式咖啡:
public class EnglishCoffee implements Beverage{
@Override
public float cost() {
return 16;
}
}
複製程式碼
拿鐵:
public class Latte implements Beverage{
@Override
public float cost() {
return 17;
}
}
複製程式碼
調料(對應抽象裝飾者Decorator):
public abstract class Decorator implements Beverage{
Beverage beverage;
public Decorator(Beverage beverage) {
this.beverage = beverage;
}
}
複製程式碼
具體調料(對應具體裝飾者Concrete Decorator):
蒸奶:
public class Milk extends Decorator{
public Milk(Beverage beverage) {
super(beverage);
}
@Override
public float cost() {
return beverage.cost() + 3;
}
}
複製程式碼
豆漿:
public class Soy extends Decorator{
public Soy(Beverage beverage) {
super(beverage);
}
@Override
public float cost() {
return beverage.cost() + 1;
}
}
複製程式碼
摩卡:
public class Mocha extends Decorator{
public Mocha(Beverage beverage) {
super(beverage);
}
@Override
public float cost() {
return beverage.cost() + 2;
}
}
複製程式碼
客戶端呼叫
點一杯美式咖啡加蒸奶、豆漿 , 以及一杯英式咖啡加蒸奶、雙份摩卡,各自計算總價:
//計算美式咖啡加蒸奶、豆漿的總價
Beverage americanCoffee = new AmericanCoffee();
float cost = new Soy(new Milk(americanCoffee)).cost();
System.out.println(cost + "");
//計算美式咖啡加蒸奶、雙份摩卡的總價
Beverage englishCoffee = new EnglishCoffee();
cost = new Mocha(new Mocha(new Milk(englishCoffee))).cost();
System.out.println(cost + "");
複製程式碼
假設不使用裝飾者模式
不使用裝飾者模式,而是都統一採用繼承的方式來解決的話,會導致類數量上呈現爆炸式增加,如下uml圖所示(只列出部分),並且這還是未考慮全部新增混合調料或者多份重複調料的情況下的一種類圖結構,數量明顯多於上面的裝飾者模式。
一旦新增加一種coffee或者新增加一種具體調料、或者修改價錢,則維護是致命的,涉及到要修改或者新增太多的類。
假設使用如下解決方法:
這種方案一旦增加一種新的調理,可能會導致所有的子類cost都需要跟著修改。並且單設增加一種產品比如”tea”是不允許新增mocha調料的,這種方案就無法避免,會引入不必要的資訊。
總結
一種動態擴充套件的方式,比繼承更加具有靈活性。
優點
- 比繼承更靈活,動態、靈活的向物件新增刪除職責,相比之下繼承要求使用前靜態建立類物件,類數量暴漲、複雜度增大
- 同一特性支援複用,比如用同一裝飾者多次裝飾(例如用”邊框”裝飾者裝飾2次就可以達到”雙邊框”的效果)
- 將特性有裝飾者實現,避免基礎類需要維護太多的特性,導致職責過於複雜
缺點
可能產生許多小物件,對不理解該結構的人來講,有點難以學習
應用場景
在不影響其他物件的情況下,以動態、透明的方式給單個物件新增職責,避免因為組合而需要建立大量的之類。