裝飾模式(Decorate Pattern)
裝飾模式定義:裝飾模式動態的將責任附加到物件上,若要擴充套件功能,裝飾模式提供了比繼承更有彈性的替代方案
看下下面的例子,總共有兩種咖啡:Decaf、Espresso,另有兩種調味品:Mocha、Whip(3種設計的主要差別在於抽象方式不同)
設計一:
即使新增再多的調味品,咖啡依然是咖啡,在抽象的過程中並沒有考慮咖啡和調味品之間的關係
當咖啡和調味品的種類很多時,將會產生大量的類,如果一種咖啡的價格發生變動,需要找到所有相關的類逐一修改
設計二:
將調味品作為Coffee類的屬性,比起設計一,類的數量大大減少,相應的,程式結構也更加清晰
public class Coffee { private boolean mocha; private boolean whip; public double cost(){ double price = 0d; if(mocha){ price += 0.5; } if(whip){ price += 0.1; } return price; } public void addMocha(){ this.mocha = true; } public void addWhip(){ this.whip = true; } }
public class Decaf extends Coffee{
public double cost(){
double price = super.cost();
price += 2.0;
return price;
}
}
public class Espresso extends Coffee {
public double cost(){
double price = super.cost();
price += 2.5;
return price;
}
}
測試一下:
public class Test { public static void main(String[] args) { Coffee coffee = new Decaf(); coffee.addMocha(); coffee.addWhip(); //2.6 System.out.println(coffee.cost()); } }
考慮到下面幾個問題,設計二有明顯的不足:
1,如果調味品的種類較多,Coffee類將會變得相當龐大,難以維護
2,類本身不夠靈活,無法處理顧客希望新增雙倍的Mocha的場景
3,新增一種新的咖啡IceCoffee,如果IceCoffee不能加Mocha,由於IceCoffee類繼承自Coffee類,IceCoffee類依然從父類繼承了addMocha()方法,這就需要在IceCoffee類中重寫一個空的addMocha()方法
設計三:裝飾模式
裝飾模式分為3個部分:
1,抽象元件Component:對應Coffee類
2,具體元件ConcreteComponent:對應具體的咖啡,如:Decaf,Espresso
3,裝飾者Decorator:對應調味品,如:Mocha,Whip
裝飾模式有3個特點:
1,具體元件和裝飾者都繼承自抽象元件(Decaf、Espresson、Mocha和Whip都繼承自Coffee),並且裝飾者持有抽象元件的引用
2,可以使用裝飾者組合具體元件創造出新的類(Mocha組合Decaf創造出MochaDecaf)
3,過程2可以重複,直到創造出需要的類
使用裝飾模式,想要獲得一個WhipDoubleMochaEspresso是很容易的:
public interface Coffee {
public double cost();
}
public class Espresso implements Coffee {
public double cost(){
return 2.5;
}
}
public class Decorator implements Coffee {
private Coffee coffee;
public Decorator(Coffee coffee){
this.coffee = coffee;
}
public double cost(){
return coffee.cost();
}
}
public class Whip extends Decorator {
public Whip(Coffee coffee){
super(coffee);
}
public double cost(){
return super.cost() + 0.1;
}
}
public class Mocha extends Decorator {
public Mocha(Coffee coffee){
super(coffee);
}
public double cost(){
return super.cost() + 0.5;
}
}
測試一下:
public class Test {
public static void main(String[] args) {
Coffee coffee = new Mocha(new Mocha(new Whip(new Espresso)));
//3.6(0.5 + 0.5 + 0.1 + 2.5)
System.out.println(coffee.cost());
}
}
看到這裡是不是想到了Java中很常用的InputStream和OutputStream類?沒錯,它們都是使用裝飾模式設計的
當然設計的時候可以靈活一些,如果只有固定一種Coffee,可以設計成下面的結構
如果只有一種調味品,下面的設計方式也是可以的
裝飾模式的缺點:
1,裝飾模式雖然擴充套件性較高,但是沒有設計二簡潔,類的數量略多(比設計一少很多),如何取捨可擴充套件性和簡潔性是個問題。如果需求比較明確,並且後期發生變化的概率不大,沒必要直接使用裝飾模式,設計二更快速高效
2,很難搞清楚一個類究竟被裝飾了多少層,可能是1層,也可能是100層
3,在某些場景下,可能需要按照一定的順序進行裝飾,稍不注意,就會產生異常
裝飾模式與建造者模式之間的區別:
裝飾模式的構造過程是不穩定的(是否需要裝飾,裝飾多少層都是可以自由調整的),建造者模式的建造過程是穩定的