結構型模式-裝飾模式(擴展系統功能)

裝飾模式能夠在不改變一個對象自己功能的基礎上給對象增長額外的新行爲。git

裝飾模式是一種用於替代繼承的技術,它經過一種無須定義子類的方式來給對象動態增長職責,使用對象之間的關聯關係取代類之間的繼承關係。在裝飾模式中引入了裝飾類,在裝飾類中既能夠調用待裝飾的原有類的方法,還能夠增長新的方法,以擴充原有類的功能。編程

1. 定義

裝飾模式(Decorator Pattern):動態地給一個對象增長一些額外的職責,就增長對象功能來講,裝飾模式比生成子類實現更爲靈活。裝飾模式是一種對象結構型模式。設計模式

2. 結構

在裝飾模式中,爲了讓系統具備更好的靈活性和可擴展性,咱們一般會定義一個抽象裝飾類,而將具體的裝飾類做爲它的子類,裝飾模式結構如圖所示:ide

裝飾模式

  • Component(抽象構件):它是具體構件和抽象裝飾類的共同父類,聲明瞭在具體構件中實現的業務方法,它的引入可使客戶端以一致的方式處理未被裝飾的對象以及裝飾以後的對象,實現客戶端的透明操做。
  • ConcreteComponent(具體構件):它是抽象構件類的子類,用於定義具體的構件對象,實現了在抽象構件中聲明的方法,裝飾器能夠給它增長額外的職責(方法)。
  • Decorator(抽象裝飾類):它也是抽象構件類的子類,用於給具體構件增長職責,可是具體職責在其子類中實現。它維護一個指向抽象構件對象的引用,經過該引用能夠調用裝飾以前構件對象的方法,並經過其子類擴展該方法,以達到裝飾的目的。
  • ConcreteDecorator(具體裝飾類):它是抽象裝飾類的子類,負責向構件添加新的職責。每個具體裝飾類都定義了一些新的行爲,它能夠調用在抽象裝飾類中定義的方法,並能夠增長新的方法用以擴充對象的行爲。
  • 因爲具體構件類和裝飾類都實現了相同的抽象構件接口,所以裝飾模式以對客戶透明的方式動態地給一個對象附加上更多的責任,換言之,客戶端並不會以爲對象在裝飾前和裝飾後有什麼不一樣。裝飾模式能夠在不須要創造更多子類的狀況下,將對象的功能加以擴展。

3. 代碼實現

原本Car只有駕駛功能,爲了動態地給Car加一些其餘的功能,好比加上一個電視,或者放幾瓶水,使用裝飾模式實現以下:性能

抽象構件類Icarthis

public interface ICar {
        void drive();
}

具體構建類1加密

public class Jeep implements ICar {

    @Override
    public void drive() {
        System.out.println("駕駛吉普車啦");
    }
}

具體構建類2設計

public class Lorry implements ICar {

    @Override
    public void drive() {
        System.out.println("開貨車啦");
    }
}

抽象裝飾類調試

public abstract class CarDecorator implements ICar {

    private ICar car;

    public CarDecorator(ICar car) {
        this.car = car;
    }

    @Override
    public void drive() {
        car.drive();
    }
}

具體裝飾類1

public class TvCarDecorator extends CarDecorator {

    public TvCarDecorator(ICar car) {
        super(car);
    }

    @Override
    public void drive() {
        addTv();
        super.drive();
    }

    public void addTv(){
        System.out.println("爲這部車安裝了電視咯");
    }
}

具體裝飾類2

public class WaterCarDecorator extends CarDecorator {

    public WaterCarDecorator(ICar car) {
        super(car);
    }

    @Override
    public void drive() {
        addWater();
        super.drive();
    }

    public void addWater(){
        System.out.println("爲車子放了幾瓶水");
    }
}

客戶端

public class Client {

    public static void main(String[] args) {
        ICar jeepCar,lorryCar,tvCar,waterCar1,waterCar2;
        jeepCar=new Jeep();
        lorryCar=new Lorry();
        tvCar=new TvCarDecorator(jeepCar);
        waterCar1=new WaterCarDecorator(tvCar);
        waterCar1.drive();
        System.out.println("------------");
        waterCar2=new WaterCarDecorator(lorryCar);
        waterCar2.drive();
    }
}

//爲車子放了幾瓶水
//爲這部車安裝了電視咯
//駕駛吉普車啦
//------------
//爲車子放了幾瓶水
//開貨車啦

能夠將裝飾了一次以後的tvCar對象注入另外一個裝飾類WaterCarDecorator中實現第二次裝飾,獲得一個通過兩次裝飾的對象waterCar1,再調用waterCar1的drive()方法便可獲得一個既有電視機又有放了水的吉普車。

4. 透明裝飾模式與半透明裝飾模式

4.1 透明裝飾模式

在透明裝飾模式中,要求客戶端徹底針對抽象編程,裝飾模式的透明性要求客戶端程序不該該將對象聲明爲具體構件類型或具體裝飾類型,而應該所有聲明爲抽象構件類型。對於客戶端而言,具體構件對象和具體裝飾對象沒有任何區別。

沒法單獨調用新增的行爲,好比

tvCar.addTv();

透明裝飾模式可讓客戶端透明地使用裝飾以前的對象和裝飾以後的對象,無須關心它們的區別,此外,還能夠對一個已裝飾過的對象進行屢次裝飾,獲得更爲複雜、功能更爲強大的對象。在實現透明裝飾模式時,要求具體裝飾類的operation()方法覆蓋抽象裝飾類的operation()方法,除了調用原有對象的operation()外還須要調用新增的addedBehavior()方法來增長新行爲。

4.2 半透明裝飾模式

爲了可以調用到新增方法,咱們不得不用具體裝飾類型來定義裝飾以後的對象,而具體構件類型仍是可使用抽象構件類型來定義,這種裝飾模式即爲半透明裝飾模式,也就是說,對於客戶端而言,具體構件類型無須關心,是透明的;可是具體裝飾類型必須指定,這是不透明的。

ICar jeepCar=new Jeep();
TvCarDecorator tvCar=new TvCarDecorator(jeepCar);
tvCar.addTv();

半透明裝飾模式能夠給系統帶來更多的靈活性,設計相對簡單,使用起來也很是方便;可是其最大的缺點在於不能實現對同一個對象的屢次裝飾(由於該對象新增的方法後面加入裝飾類的時候仍是沒法調用,因此是不完全的裝飾),並且客戶端須要有區別地對待裝飾以前的對象和裝飾以後的對象。

5. 注意事項

  1. 儘可能保持裝飾類的接口與被裝飾類的接口相同,這樣,對於客戶端而言,不管是裝飾以前的對象仍是裝飾以後的對象均可以一致對待。這也就是說,在可能的狀況下,咱們應該儘可能使用透明裝飾模式
  2. 儘可能保持具體構件類ConcreteComponent是一個「輕」類,也就是說不要把太多的行爲放在具體構件類中,咱們能夠經過裝飾類對其進行擴展。
  3. 若是隻有一個具體構件類,那麼抽象裝飾類能夠做爲該具體構件類的直接子類。(不須要定義抽象構建了,可是之後無法擴展新的具體構建類)

6. 優缺點

  • 優勢
  1. 對於擴展一個對象的功能,裝飾模式繼承更加靈活性,不會致使類的個數急劇增長。
  2. 能夠經過一種動態的方式來擴展一個對象的功能,經過配置文件能夠在運行時選擇不一樣的具體裝飾類,從而實現不一樣的行爲。
  3. 能夠對一個對象進行屢次裝飾,經過使用不一樣的具體裝飾類以及這些裝飾類的排列組合,能夠創造出不少不一樣行爲的組合,獲得功能更爲強大的對象。
  4. 具體構件類與具體裝飾類能夠獨立變化,用戶能夠根據須要增長新的具體構件類和具體裝飾類,原有類庫代碼無須改變,符合「開閉原則」
  • 缺點
  1. 使用裝飾模式進行系統設計時將產生不少小對象,這些對象的區別在於它們之間相互鏈接的方式有所不一樣,而不是它們的類或者屬性值有所不一樣,大量小對象的產生勢必會佔用更多的系統資源,在必定程序上影響程序的性能。
  2. 裝飾模式提供了一種比繼承更加靈活機動的解決方案,但同時也意味着比繼承更加易於出錯,排錯也很困難,對於屢次裝飾的對象,調試時尋找錯誤可能須要逐級排查,較爲繁瑣。

7. 適用場景

  1. 在不影響其餘對象的狀況下,以動態、透明的方式給單個對象添加職責。
  2. 當不能採用繼承的方式對系統進行擴展或者採用繼承不利於系統擴展和維護時可使用裝飾模式。不能採用繼承的狀況主要有兩類:第一類是系統中存在大量獨立的擴展,爲支持每一種擴展或者擴展之間的組合將產生大量的子類,使得子類數目呈爆炸性增加;第二類是由於類已定義爲不能被繼承(如Java語言中的final類)。

8. 我的理解

裝飾模式下降了系統的耦合度,能夠動態增長或刪除對象的職責,並使得須要裝飾的具體構件類和具體裝飾類能夠獨立變化,以便增長新的具體構件類和具體裝飾類。在JavaIO中的輸入流和輸出流的設計、javax.swing包中一些圖形界面構件功能的加強,多重加密的設計能夠運用裝飾模式。

參考

  1. Java設計模式-劉偉
相關文章
相關標籤/搜索