定義
23種設計模式之一,英文叫Decorator Pattern,又叫裝飾者模式。裝飾模式是在不必改變原類檔案和使用繼承的情況下,動態地擴展一個對象的功能。它是通過創建一個包裝對象,也就是裝飾來包裹真實的對象。
特點
(1) 裝飾對象和真實對象有相同的接口。這樣客戶端對象就能以和真實對象相同的方式和裝飾對象互動。
(2) 裝飾對象包含一個真實對象的引用(reference)
(3) 裝飾對象接受所有來自客戶端的請求。它把這些請求轉發給真實的對象。
(4) 裝飾對象可以在轉發這些請求以前或以後增加一些附加功能。這樣就確保了在運行時,不用修改給定對象的結構就可以在外部增加附加的功能。在面向對象的設計中,通常是通過繼承來實現對給定類的功能擴展。
適用性
以下情況使用Decorator模式
1. 需要擴展一個類的功能,或給一個類添加附加職責。
2. 需要動態的給一個對象添加功能,這些功能可以再動態的撤銷。
3. 需要增加由一些基本功能的排列組合而產生的非常大量的功能,從而使繼承關係變的不現實。
4. 當不能採用生成子類的方法進行擴充時。一種情況是,可能有大量獨立的擴展,為支持每一種組合將產生大量的子類,使得子類數目呈爆炸性增長。另一種情況可能是因為類定義被隱藏,或類定義不能用於生成子類。
優點
1. Decorator模式與繼承關係的目的都是要擴展對象的功能,但是Decorator可以提供比繼承更多的靈活性。
2. 通過使用不同的具體裝飾類以及這些裝飾類的排列組合,設計師可以創造出很多不同行為的組合。
缺點
1. 這種比繼承更加靈活機動的特性,也同時意味著更加多的複雜性。
2. 裝飾模式會導致設計中出現許多小類,如果過度使用,會使程式變得很複雜。
3. 裝飾模式是針對抽象組件(Component)類型編程。但是,如果你要針對具體組件編程時,就應該重新思考你的套用架構,以及裝飾者是否合適。當然也可以改變Component接口,增加新的公開的行為,實現“半透明”的裝飾者模式。在實際項目中要做出最佳選擇。
設計原則
1. 多組合,少繼承。
利用繼承設計子類的行為,是在編譯時靜態決定的,而且所有的子類都會繼承到相同的行為。然而,如果能夠利用組合的做法擴展對象的行為,就可以在運行時動態地進行擴展。
2. 類應設計的對擴展開放,對修改關閉。
模式簡化
1. 如果只有一個Concrete Component類而沒有抽象的Component接口時,可以讓Decorator繼承Concrete Component。
2. 如果只有一個Concrete Decorator類時,可以將Decorator和Concrete Decorator合併。
相關區別
1.關於新職責:適配器也可以在轉換時增加新的職責,但主要目的不在此。裝飾者模式主要是給被裝飾者增加新職責的。
2.關於原接口:適配器模式是用新接口來調用原接口,原接口對新系統是不可見或者說不可用的。裝飾者模式原封不動的使用原接口,系統對裝飾的對象也通過原接口來完成使用。(增加新接口的裝飾者模式可以認為是其變種--“半透明”裝飾者)
3.關於其包裹的對象:適配器是知道被適配者的詳細情況的(就是那個類或那個接口)。裝飾者只知道其接口是什麼,至於其具體類型(是基類還是其他派生類)只有在運行期間才知道。
實際使用
java IO 流是典型的裝飾模式。
代碼示例
在裝飾模式中的各個角色有:
(1)抽象構件(Component)角色:給出一個抽象接口,以規範準備接收附加責任的對象。
(2)具體構件(Concrete Component)角色:定義一個將要接收附加責任的類。
(3)裝飾(Decorator)角色:持有一個構件(Component)對象的實例,並實現一個與抽象構件接口一致的接口。
(4)具體裝飾(Concrete Decorator)角色:負責給構件對象添加上附加的責任。
以下示例中,ThirdParty.Java假定是一個現有的或者第三方的功能,因某種原因我們不能直接修改,它提供了一個sayMsg()的方法,而我們現在要做的是想在它的sayMsg()方法中增加一些我們想額外輸出的內容,於是我們重寫了一個Decorator.java類。MailTest.java是客戶端測試程式。
IthirdParty.Java--抽象接口類
=====================
package decorator.saystr;
public interface IThirdParty {
public String sayMsg();
}
ThirdParty.Java--具體類
===================
public class ThirdParty implements IThirdParty {
public String sayMsg() {
return "hello";
}
}
Decorator1.java 具體裝飾類1
==================
package decorator.saystr;
public class Decorator1 implements IThirdParty {
private IThirdParty thirdParty;
public Decorator1(IThirdParty thirdParty){
this.thirdParty= thirdParty;
}
public String sayMsg(){
return "##1"+ thirdParty.sayMsg() + "##1";
}
}
Decorator1.java 具體裝飾類2
==================
package decorator.saystr;
public class Decorator2 implements IThirdParty {
private IThirdParty thirdParty;
public Decorator2(IThirdParty thirdParty){
this.thirdParty= thirdParty;
}
public String sayMsg(){
return "##2"+ thirdParty.sayMsg() + "##2";
}
}
MailTest.java
====================
package decorator.saystr;
public class MailTest {
public static void main(String[] args){
IThirdParty thirdPartyOne =new ThirdParty();
IThirdParty decorator1 =new Decorator1(thirdPartyOne);
IThirdParty decorator2 =new Decorator2(decorator1);
System.out.println(decorator2.sayMsg());
}
}
執行結果是:##2##1hello##1##2