1、簡介
裝飾模式(Decorator)也叫包裝器模式(Wrapper)。GOF在《設計模式》一書中給出的定義為:動態地給一個對象添加一些額外的職責。就增加功能來說,Decorator模式相比生成子類更為靈活。請關注兩個重點:
動態:也就是說無法預先在編譯階段就知道職責的具體內容和添加時機,什么時候添加什么功能完全由客戶在運行時刻規定。
增加:“Decorator模式”往往用于在現有功能的基礎上針對不同的對象,添加一些不同的功能,實際上我們還可以改變行為的流程。
2、使用場景及能解決的問題
有時候在同一個運行環境下,由于不同對象具有不同的特性導致了它們在執行同一種類型操作時,可能存在一些細小的差別,比如:參數不同、過程不同、方式不同等。
那么針對這三種情況我們可以采取的解決方法有兩種:
采用繼承的方式:定義一個父類的抽象方法,由不同的子類各自實現不同的操作,但一旦子類一多勢必帶來類的數量膨脹的問題,難以管理;其次如果只是一些局部的細微差別而導致了必須重寫整個過程或方法,那么必將導致出現大量的重復代碼(即功能相同部分的代碼)。有些項目,里面存在了大量的重復代碼,到處都是“Copy-Paste”,只是修改了某處的關鍵參數而已。
而“Duplicate code”正是《重構-改善既有代碼的設計》一書中提到的首要“異味”,這意味著一旦公共部分的功能改變了,那么你有可能需要重新編譯幾個甚至十幾個子類。
采用“Decorator模式”:首先將功能分解,分成若干個小塊,這是為了后面我們可以動態地重組。其次將功能部分抽取出來成為一個接口,通過一個實現了接口的基類完成基本的共有功能。一旦我們需要在基類的功能基礎上添加或改變流程,我們就可以通過“Decorator類”實現接口,在接口的實現方法中增加、改變方法的功能或流程。
比較上面這兩種做法,我們可以看到使用了“Decorator模式”后,代碼更加簡潔了,可重用性和可擴展性更高了。其次由于可以在構造 “Decorator類”的過程中傳遞參數,我們可以動態的增加、改變原有的功能、流程,而相同部分的功能則委托給基類去完成。
所以裝飾模式適用的場景包括:
需要擴展一個類的功能,或給一個類增加附加責任。
需要動態地給一個對象增加功能,這些功能可以再動態地撤銷。
需要增加由一些基本功能的排列組合而產生的非常大量的功能,從而使繼承關系變得不現實。
3、類圖

4、實例
裝飾模式涉及到一下幾個角色:
* 抽象構件(Component)角色:給出一個抽象接口,以規范準備接收附加責任的對象。
* 具體構件(Concrete Component)角色:定義一個將要接收附加責任的類。
* 裝飾(Decorator)角色:持有一個構件(Component)對象的實例,并定義一個與抽象構件接口一致的接口。
* 具體裝飾(Concrete Decorator)角色:負責給構件對象"貼上"附加的責任。
下面分別看一下這幾個角色的實現:
Component:
package decorator;
public interface Mobile {
void call();
void sendMessage();
}
Concrete Component:
package decorator;
public class MotoMobile implements Mobile {
public void call(){
System.out.print("call somebody with Moto");
}
public void sendMessage(){
System.out.print("send message to somebody with Moto");
}
}
package decorator;
public class NokiaMobile implements Mobile {
public void call(){
System.out.print("call somebody with Nokia");
}
public void sendMessage(){
System.out.print("send message to somebody with Nokia");
}
}
Decorator:
package decorator;
public class DecoratorMobile implements Mobile {
private Mobile mobile;
public DecoratorMobile(Mobile mobile){
this.mobile=mobile;
}
public void call(){
this.mobile.call();
}
public void sendMessage(){
this.mobile.sendMessage();
}
}
Concrete Decorator:
package decorator;
public class MobileGPS extends DecoratorMobile {
public MobileGPS(Mobile mobile){
super(mobile);
}
public void call(){
super.call();
System.out.println(":use GPS");
}
public void sendMessage(){
super.sendMessage();
System.out.println(":use GPS");
}
}
package decorator;
public class MobileBlueTooth extends DecoratorMobile {
public MobileBlueTooth(Mobile mobile){
super(mobile);
}
public void call(){
super.call();
System.out.println(":use BlueTooth");
}
public void sendMessage(){
super.sendMessage();
System.out.println(":use BlueTooth");
}
}
最后看看調用的客戶端:
package decorator;
public class Client {
private static Mobile mobile=new MotoMobile();
private static MobileGPS mobileGPS=new MobileGPS(mobile);
private static MobileBlueTooth mobileBlueTooth=new MobileBlueTooth(mobile);
public static void main(String args[]){
mobileGPS.call();
mobileGPS.sendMessage();
mobileBlueTooth.call();
mobileBlueTooth.sendMessage();
}
}
5、優缺點
比靜態繼承更靈活:與對象的靜態繼承相比,Decorator模式提供了更加靈活的向對象添加職責的方式,可以使用添加和分離的方法,用裝飾在運行時刻增加和刪除職責。
避免在層次結構高層的類有太多的特征:Decorator模式提供了一種“即插即用”的方法來添加職責,他并不試圖在一個復雜的可訂制的類中支持所有可預見的特征,相反可以定義一個簡單的類,并且用Decorator類給他逐漸的添加功能,可以從簡單的部件組合出復雜的功能。
會產生比使用繼承關系更多的對象,使得維護和排查問題略顯復雜。
6、Decorator模式與Adapater、Builder模式的區別
Adapter和Decorator模式的區別:
前者關注的是如何將兩個本來互不相關的但又有相似功能的類糅合到一起,后者關注的是如何在原來功能的基礎上做一些小的調整。
Builder和Decorator模式的區別:
前者關注的是對象的構建,后者關注的是對象功能的添加、個性化。
前者主要用于GUI界面構建對象的場合、后者主要用于動態添加功能、改變流程的場合。