1. 程式人生 > >Java設計模式學習記錄-裝飾模式

Java設計模式學習記錄-裝飾模式

ora 動態 裝飾模式 his 開閉原則 不能 ood controls 結構型模式

前言

裝飾模式也是一種結構型模式,主要是目的是相對於類與類之間的繼承關系來說,使用裝飾模式可以降低耦合度。JDK中有不少地方都使用到了裝飾模式,例如Java的各種I/O流,javax.swing包中一些圖形界面構件功能的增強等地方都運用了裝飾模式。

裝飾模式

定義

裝飾模式的定義是:在不改變原類文件以及不使用繼承的情況下,動態的擴展一個對象的功能。裝飾模式是通過創建一個包裝對象來實現的,也就是用裝飾來包裹真實的對象。

舉例

還是老規矩,舉例說明,在給親朋好友過生日時會買生日蛋糕,然後生日蛋糕又有各種各樣的輔料來進行裝飾,例如:奶油,水果,芝士,巧克力等等。如果沒有這些輔料來進行裝飾,就是普通的雞蛋糕。所以我們可以把做蛋糕的這個過程來用代碼實現出來:

首先定義蛋糕接口

/**
 * 蛋糕
 */
public interface CakeGoods {

    /**
     * 展示蛋糕
     */
    void showCake();

    /**
     * 展示價格
     */
    BigDecimal showPrice();
}

具體的雞蛋糕

/**
 * 雞蛋糕
 */
@Data
public class SpongeCake implements CakeGoods{

    //蛋糕名稱
    private String name;
    //蛋糕價格
private BigDecimal price; public SpongeCake(String name,BigDecimal price){ this.name = name; this.price = price; } /** * 展示蛋糕 */ @Override public void showCake() { System.out.println("的"+this.name+"蛋糕"); } /** * 展示價格
*/ @Override public BigDecimal showPrice() { return this.price; } }

輔料的抽象類

/**
 * 輔料的抽象類
 */
public abstract class Decorator implements CakeGoods{

    private CakeGoods cakeGoods;

    public void setCakeGoods(CakeGoods cakeGoods){
        this.cakeGoods = cakeGoods;
    }

    /**
     * 展示蛋糕
     */
    @Override
    public void showCake() {
        cakeGoods.showCake();
    }

    /**
     * 展示價格
     */
    @Override
    public BigDecimal showPrice() {
        return cakeGoods.showPrice();
    }
}

水果

/**
 * 水果
 */
public class Fruits extends Decorator {
    /**
     * 展示蛋糕
     */
    @Override
    public void showCake() {
        System.out.print("加水果");
        super.showCake();
    }

    /**
     * 展示價格
     */
    @Override
    public BigDecimal showPrice() {
        return new BigDecimal(20.00).add(super.showPrice());
    }
}

奶油

/**
 * 奶油
 */
public class Cream extends Decorator {
    /**
     * 展示蛋糕
     */
    @Override
    public void showCake() {
        System.out.print("加奶油");
        super.showCake();
    }

    /**
     * 展示價格
     */
    @Override
    public BigDecimal showPrice() {
        return new BigDecimal(15.00).add(super.showPrice());
    }
}

測試

public class Tests {

    public static void main(String[] args) {
        
        //制造雞蛋糕
        CakeGoods cakeGoods = new SpongeCake("生日祝福",new BigDecimal(50));

        Decorator cream = new Cream();
        Decorator fruits = new Fruits();

        //加奶油
        cream.setCakeGoods(fruits);
        //加水果
        fruits.setCakeGoods(cakeGoods);
        
        //展示
        cream.showCake();
        System.out.println("的價格是:"+cream.showPrice()+"元");
    }

}

運行結果:

加奶油加水果的生日祝福蛋糕
的價格是:85元

上面的這個蛋糕制造的例子使用的就是裝飾模式,在為雞蛋糕SpongeCake進行擴展的時候並沒有影響它原來的類的結構,也沒有使用繼承的關系,最終卻達到了裝飾的目的。下面我們來分析一下裝飾模式具體是由那幾部分組成。

裝飾模式的結構

技術分享圖片

裝飾模式的結構圖如上所示,主要由以下幾個角色組成。

抽象構件角色:(上圖的CakeGoods)定義一個抽象接口,以規範準備接受裝飾的對象,想當於Java中的IO流裏的InputStram/OutputStream和Reader/Writer。

具體的構件角色:(上圖的SpongeCake)定義一個將要接受裝飾的類,相當於I/O流裏面的FileOutputStream和FileInputStream。

裝飾角色:(上圖的Decorator)定義一個持有抽象構件角色的引用,並定義一個與抽象構件一直的接口。相當於I/O裏面的FilterOutputStream和FilterInputStream。

具體的裝飾角色:(上圖的Fruits和Cream)負責各構件角色對象加上相應的裝飾品,相當於I/O流裏的BufferedOutputStream、BufferedInputStream。

在使用的時候,必須擴展CakeGoods的功能,但是我們是通過Fruits和Cream來對CakeGoods進行擴展的,所以相對於CakeGoods來說無需知道Decorator的存在,就能擴展功能了。

總結

裝飾模式的優點

  1. 對於擴展一個對象的功能,裝飾模式比繼承更加靈活性,不會導致類的個數急劇增加。例如上面的例子如果想增加一個棗糕或是一個巧克力輔料,無需修改現有代碼,只需將棗糕作為CakeGoods的一個子類,以及Decorator的一個子類即可。
  2. 可以動態的擴展一個對象的功能。
  3. 可以對一個對象進行多次裝飾,通過使用不同的具體裝飾類以及這些裝飾類的排列組合,可以創造出很多不同行為的組合,得到功能更為強大的對象。
  4. 具體構件類與具體裝飾類可以獨立變化,用戶可以根據需要增加新的具體構件類和具體裝飾類,原有類庫代碼無須改變,符合“開閉原則”。

裝飾模式的缺點

  1. 使用裝飾模式進行系統設計時將產生很多小對象,這些對象的區別在於它們之間相互連接的方式有所不同,而不是它們的類或者屬性值有所不同,大量小對象的產生勢必會占用更多的系統資源,在一定程序上影響程序的性能。
  2. 裝飾模式提供了一種比繼承更加靈活機動的解決方案,但同時也意味著比繼承更加易於出錯,排錯也很困難,對於多次裝飾的對象,調試時尋找錯誤可能需要逐級排查,較為繁瑣。

適用場景

在不影響其他對象的情況下,以動態、透明的方式給單個對象添加職責。

當不能采用繼承的方式對系統進行擴展或者采用繼承不利於系統擴展和維護時可以使用裝飾模式。不能采用繼承的情況主要有兩類:第一類是系統中存在大量獨立的擴展,為支持每一種擴展或者擴展之間的組合將產生大量的子類,使得子類數目呈爆炸性增長;第二類是因為類已定義為不能被繼承(如Java語言中的final類)。

加油,給自己打氣,克服惰性!!!

想了解更多的設計模式請查看Java設計模式學習記錄-GoF設計模式概述。

Java設計模式學習記錄-裝飾模式