裝飾模式

概述


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 Comonent。
  2. 如果只有一個Concrete Decorator類時,可以將Decorator和Concrete Decorator合併。

裝飾者與適配者模式的區別


  1. 關於新職責:適配器也可以在轉換時增加新的職責,但主要目的不在此。裝飾者模式主要是給被裝飾者增加新職責的。
  2. 關於原接口:適配器模式是用新接口來調用原接口,原接口對新系統是不可見或者說不可用的。裝飾者模式原封不動的使用原接口,系統對裝飾的對象也通過原接口來完成使用。(增加新接口的裝飾者模式可以認爲是其變種--“半透明”裝飾者)
  3. 關於其包裹的對象:適配器是知道被適配者的詳細情況的(就是那個類或那個接口)。裝飾者只知道其接口是什麼,至於其具體類型(是基類還是其他派生類)只有在運行期間才知道。

實際使用


    Java IO 流是典型的裝飾模式。

代碼示例


    在裝飾模式中的各個角色有:
    (1) 抽象構件(Component)角色:給出一個抽象接口,以規範準備接收附加責任的對象。
    (2) 具體構件(Concrete Component)角色:定義一個將要接收附加責任的類。
    (3) 裝飾(Decorator)角色:持有一個構件(Component)對象的實例,並實現一個與抽象構件接口一致的接口。
    (4) 具體裝飾(Concrete Decorator)角色:負責給構件對象添加上附加的責任。
    以下示例中,Human.java假定是一個現有的或者第三方的功能,因某種原因我們不能直接修改,它提供了兩個方法wearClothes(), walkToWhere(),而我們現在要做的是想在它的wearClothes(), walkToWhere()方法中增加一些我們想額外輸出的內容,於是我們重寫了一個Decorator.java類。Test是客戶端測試程序。
// 定義被裝飾者 -- 抽象構件角色
public interface Human {
public void wearClothes();
 
public void walkToWhere();
}
 
//定義被裝飾者,被裝飾者初始狀態有些自己的裝飾 -- 具體構件角色
public class Person implements Human {
 
@Override
public void wearClothes() {
System.out.println("穿什麼呢...");
}
 
@Override
public void walkToWhere() {
System.out.println("去哪裏呢...");
}
 
}
 
// 定義裝飾者 -- 裝飾角色
public abstract class Decorator implements Human{
private Human human;
public Decorator(Human human) {
this.human = human;
}
public void wearClothes() {
human.wearClothes();
}
public void walkToWhere() {
human.walkToWhere();
}
}
// 具體構件角色  
public class Decorator_zero extends Decorator {
 
public Decorator_zero(Human human) {
super(human);
}
 
public void getHome() {
System.out.println("進房子...");
}
public void findMap() {
System.out.println("書房找找map...");
}
@Override
public void wearClothes() {
super.wearClothes();
getHome();
}
@Override
public void walkToWhere() {
super.walkToWhere();
findMap();
}
}
 
//具體構件角色
public class Decorator_first extends Decorator {
 
public Decorator_first(Human human) {
super(human);
}
public void goColthespress() {
System.out.println("去衣櫃找找看...");
}
public void findPlaceOnMap() {
System.out.println("在Map上找找..");
}
 
@Override
public void wearClothes() {
super.wearClothes();
goColthespress();
}
@Override
public void walkToWhere() {
super.walkToWhere();
findPlaceOnMap();
}
}
 
//具體構件角色
public class Decorator_two extends Decorator {
 
public Decorator_two(Human human) {
super(human);
}
public void findClothes() {
System.out.println("找到一件D&G。。");
}
public void findTheTarget() {
System.out.println("在Map上找到神祕花園和城堡。。");
}
@Override
public void wearClothes() {
super.wearClothes();
findClothes();
}
@Override
public void walkToWhere() {
super.walkToWhere();
findTheTarget();
}
 
}
 
// 測試類
public class Test {
 
public static void main(String[] args) {
Human person = new Person();
Decorator decorator = new Decorator_two(new Decorator_first(
new Decorator_zero(person)));
decorator.wearClothes();
decorator.walkToWhere();
}
 
}
結果輸出
穿什麼呢...
進房子...
去衣櫃找找看...
找到一件D&G。。
去哪裏呢...
書房找找map...
在Map上找找..
在Map上找到神祕花園和城堡。。

參考資料





















發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章