23種設計模式(三)--裝飾器模式


裝飾器模式

一. 什么是裝飾器模式?

我們都知道裝飾, 元旦, 聖誕節, 我們都需要裝飾, 渲染節日氣氛. . 所謂裝飾, 就是在原來的基礎上加東西.

裝飾器模式(Decorator Pattern)允許向一個現有的對象添加新的功能,同時又不改變其結構。這種類型的設計模式屬於結構型模式,它是對現有類的包裝。

這種模式創建了一個裝飾類,用來包裝原有的類,並在保持類方法簽名完整性的前提下,提供了額外的功能。
我們通過下面的實例來演示裝飾器模式的用法。其中,我們將把聖誕節的房間在原來的基礎上裝飾上了氣球。

我們來看看UML圖:
最開始有房子, 房子有平房, 樓房, 別墅, 我們會打掃房間.

/**
 * 房子抽象類
 */
public interface IHouse {
    void clean();
}

/**
 * 平房
 */
public class Bungalow implements IHouse{
    @Override
    public void clean() {
        // 打掃房間
    }
}

/**
 * 樓房
 */
public class HighRiseBuilding implements IHouse{
    @Override
    public void clean() {
        // 打掃房間
    }
}

/**
 * 別墅
 */
public class Villa implements IHouse{
    @Override
    public void clean() {
        // 打掃房間
    }
}

這時, 遇到節假日, 比如:聖誕節, 元旦, 我們會裝飾房間, 給房間布置氣球等.
我們可以采用裝飾器設計模式. 這時, 裝飾的主體依然房子. 裝飾就像是一個殼子, 套在房子的外面.

/**
 * 房子抽象類
 */
public interface IHouse {
    void clean();
}

/**
 * 平房
 */
public class Bungalow implements IHouse{
    @Override
    public void clean() {
        // 打掃房間
        System.out.println("我是平房: 打掃房間");
    }
}

/**
 * 樓房
 */
public class HighRiseBuilding implements IHouse{
    @Override
    public void clean() {
        // 打掃房間
        System.out.println("我是樓房: 打掃房間");
    }
}

/**
 * 別墅
 */
public class Villa implements IHouse{
    @Override
    public void clean() {
        // 打掃房間
        System.out.println("我是別墅: 打掃房間");
    }
}

/**
 * 房間裝飾器
 */
public abstract class DecoratorHouse implements IHouse {
    // 被裝飾的房子
    protected IHouse decoratedHouse;

    public DecoratorHouse(IHouse decoratedHouse) {
        this.decoratedHouse = decoratedHouse;
    }

}

public class BalloonDecoratorHouse extends DecoratorHouse {


    public BalloonDecoratorHouse(IHouse decoratedHouse) {
        super(decoratedHouse);
    }

    @Override
    public void clean() {
        // 打掃房間
        super.decoratedHouse.clean();
        ballonDecorate();
    }

    public void ballonDecorate() {
        // 用氣球裝飾房間
        System.out.println("用氣球裝飾房間, 好漂亮");
    }
}

public class DecoratorPatternTest {
    public static void main(String[] args) {
        IHouse house = new Villa();
        house.clean();

        System.out.println("-------聖誕節到了, 裝飾房間-------");
        IHouse decoratorHouse = new BalloonDecoratorHouse(new Villa());
        decoratorHouse.clean();
    }
}

使用裝飾器模式以后, 我們在不改變原有房子的基礎上進行了擴展. 節假日, 就可以使用裝飾類裝飾房間, 如果節日過去了, 我們就繼續使用原來的房子.

接下來, 我們來總結一些上面的案例.
首先, 我們有一個組件Component, 在這個組件里有一些自定義的功能. 通常這個組件Component是抽象的(接口或者抽象類)
然后, 抽象類會有一個具體的實現 ConcreteComponent, 除了實現組件Component的方法, 還可自定義方法.
第三, 我們現在想要對具體實現ConcreteComponent進行包裝, 定義一個包裝類 DecoratorComponent. 通常這個包裝類是抽象的. 包裝類也實現組件Component接口, 然后引入一個Component的具體的成員變量. 為什么要引入成員變量呢? 這個也很好理解, 我們的目標是包裝的具體類.
第四, 定義一個具體的裝飾器 DecoratorComponent, 再具體的裝飾器中, 可以增加額外的方法. 比如在之前后者之后增加一些邏輯. 具體關系圖如下:

代碼實現如下:

/**
 * 抽象功能
 */
public interface Component {
    void operate();
}

/**
 * 具體功能實現
 */
public class ConcreteComponent implements Component{
    @Override
    public void operate() {
        // 實現具體的邏輯
        System.out.println("具體實現的邏輯");
    }
}

/**
 * 用來裝飾Component對象的類
 */
public abstract class DeceratorComponent implements Component{
    // 指定裝飾的對象
    protected Component deceratedComponent;

    public DeceratorComponent(Component deceratedComponent) {
        this.deceratedComponent = deceratedComponent;
    }
}

/**
 * 具體的裝飾類
 */
public class ConcreteDeceratorComponent extends DeceratorComponent {

    public ConcreteDeceratorComponent(Component deceratedComponent) {
        super(deceratedComponent);
    }

    @Override
    public void operate() {
        before();
        super.deceratedComponent.operate();
        after();
    }

    public void before(){
        System.out.println("在原邏輯之前增加了邏輯");
    }

    public void after(){
        System.out.println("在原邏輯之后增加了邏輯");
    }

    
}

public class DeceratorTest {
    public static void main(String[] args) {
        Component concreteCom = new ConcreteComponent();
        concreteCom.operate();

        System.out.println("============");
        DeceratorComponent decerator = new ConcreteDeceratorComponent(new ConcreteComponent());
        decerator.operate();

    }
}

運行結果:

具體實現的邏輯

============

在原邏輯之前增加了邏輯
具體實現的邏輯
在原邏輯之后增加了邏輯

設計模式的靈活應用. 假如當前具體類就只有一個. 我們就不需要定義抽象的Component了. 如何實現裝飾模式呢?
那就讓裝飾器直接繼承自原來的類就可以了:

二. 裝飾器模式的特點:

  1. 裝飾對象和真實對象有相同的接口。這樣客戶端對象就能以和真實對象相同的方式和裝飾對象交互。
  2. 裝飾對象包含一個真實對象的引用(reference)
  3. 裝飾對象接受所有來自客戶端的請求。它把這些請求轉發給真實的對象。
  4. 裝飾對象可以在轉發這些請求以前或以后增加一些附加功能。這樣就確保了在運行時,不用修改給定對象的結構就可以在外部增加附加的功能。在面向對象的設計中,通常是通過繼承來實現對給定類的功能擴展。

三. 裝飾器模式的使用場景:

以下情況可以考慮使用裝飾器模式

  1. 需要擴展一個類的功能,或給一個類添加附加職責。
  2. 需要動態的給一個對象添加功能,這些功能可以再動態的撤銷。
  3. 需要增加由一些基本功能的排列組合而產生的非常大量的功能,從而使繼承關系變的不現實。
  4. 當不能采用生成子類的方法進行擴充時。一種情況是,可能有大量獨立的擴展,為支持每一種組合將產生大量的子類,使得子類數目呈爆炸性增長。另一種情況可能是因為類定義被隱藏,或類定義不能用於生成子類。

四. 裝飾器模式的優點:

  1. Decorator模式與繼承關系的目的都是要擴展對象的功能,但是Decorator可以提供比繼承更多的靈活性。
  2. 通過使用不同的具體裝飾類以及這些裝飾類的排列組合,設計師可以創造出很多不同行為的組合。

五. 裝飾器模式的缺點

  1. 這種比繼承更加靈活機動的特性,也同時意味着更加多的復雜性。
  2. 裝飾模式會導致設計中出現許多小類,如果過度使用,會使程序變得很復雜。
  3. 裝飾模式是針對抽象組件(Component)類型編程。但是,如果你要針對具體組件編程時,就應該重新思考你的應用架構,以及裝飾者是否合適。當然也可以改變Component接口,增加新的公開的行為,實現“半透明”的裝飾者模式。在實際項目中要做出最佳選擇。

六. 思考: 裝飾器模式使用了哪些設計模式的原則?

  1. 最明顯的體現就是開閉原則---對擴展開發, 對修改關閉. 當新的需求到達, 在不改變原來功能的基礎上進行擴展.
  2. 依賴倒置原則---依賴於抽象, 而非具體. 方便擴展, 再來一種新房子, 不用修改房子裝飾類.
  3. 單一職責原則: 一個類只負責一件事


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM