1. 程式人生 > >設計模式05----裝飾者模式

設計模式05----裝飾者模式

1. 什麼時候使用裝飾者模式

比如有一家店賣飲品,飲品就有不少種,每一種還可以加項,比如給可樂加冰,加糖,兌水什麼的,每次加項的價格還不同,就會將程式碼弄的很繁瑣,這種情況下就可以使用裝飾者模式來實現. 

何時使用

2. 什麼是裝飾者模式

上述的例子中,可以以飲品為主體,用不用的各種需求來裝飾它,比如有一個可樂物件,那我用一個加冰物件裝飾一下,再用加糖物件裝飾一下,最後能得到一個加冰加糖可樂,這時候就將原可樂物件擴充套件,得到了加冰和加糖兩種裝飾. 
裝飾者模式: 動態地將責任附加到物件上,對擴充套件功能來說,裝飾者比繼承更有彈性更靈活(因為子類繼承父類擴充套件功能的前提,是已知要擴充套件的功能是什麼樣的,而這是在編譯時就要確定的,但是裝飾者模式可以實現動態(在執行時)去擴充套件功能).
3. 裝飾者模式結構

Decorator:是裝飾者的父類,每個裝飾者都需要繼承這個抽象類(或實現這個介面). 
ConcreteDecoratorA/B:具體的裝飾者,就對應上述例子中的加冰,加糖等. 
ConcreteComponent:具體的物件,就是上述例子中的可樂. 
Component:裝飾者模式中最頂級的父類,裝飾者與被裝飾者都是它的子類或實現類才行.
4. 舉例說明

(1)建立最頂級的父類

飲品類:所有的被裝飾的類都需要繼承它.

package componet;

/**
 * Created by zyf on 2017/3/30.
 * 裝飾者模式中最頂級的父類
 */
public abstract class 飲品 {
    String name;

    /**
     * 每個飲品的價格不同,所以講price方法抽象化<br/>
     * 讓每個實現"飲品"類的子類自己決定是多少錢
     * */
    public abstract int price();

    /***
     * 得到飲品的名字
     * @return 名字
     */
    public String getName(){
        return name;
    }
}

(2)被裝飾的可樂Component類

package componet;

/**
 * Created by zyf on 2017/3/30.
 */
public class 可樂Component extends 飲品 {

    public 可樂Component() {
        //設定name為可樂
        //這個name屬性是從飲品類中繼承來的
        name = "可樂";
    }

    /***
     * 實現父類的抽象方法
     * @return 可樂的價格
     */
    @Override
    public int price() { //可樂30塊一瓶~ return 30; } }

(3)被裝飾的啤酒Component類

package componet;

/**
 * Created by zyf on 2017/3/30.
 */
public class 啤酒Component  extends 飲品{
    public 啤酒Component() {
        //設定name為啤酒
        //這個name屬性是從飲品類中繼承來的
        name = "啤酒";
    }

    /***
     * 實現父類的抽象方法
     * @return 啤酒的價格
     */
    @Override
    public int price() { //啤酒3塊一瓶~ return 3; } }

(4)Decorator類,所有裝飾類的父類

package decorator;

import componet.飲品;

/**
 * Created by zyf on 2017/3/30.
 * 裝飾者模式中,所有裝飾者的父類
 */
public abstract class Decorator extends 飲品 {

    /***
     * 宣告一個飲品引用,準備接受一個飲品物件<br/>
     */
    protected 飲品 yp;

    public Decorator(飲品 yp) {
        this.yp = yp; } }

(5)裝飾類:加醋Decorator類

package decorator;

import componet.飲品;

/**
 * Created by zyf on 2017/3/30.
 */
public class 加醋Decorator extends Decorator {


    public 加醋Decorator(飲品 yp) {
        super(yp);
    }

    public void addVinegar(){ System.out.println("還要加醋,加完了"); } /*** * 那麼加醋後的價格應該是多少呢?<br/> * 應該是加粗的價格加飲品的價格 * @return 加醋五塊 */ @Override public int price() { return 5 + yp.price(); } /*** * 再複寫一個名字的方法<br/> * 現在已經不是單純的飲品了 * @return */ @Override public String getName() { //在這裡加個醋  addVinegar(); return "加醋的" + yp.getName(); } }

(6)裝飾類:兌水Decorator類

package decorator;

import componet.飲品;

/**
 * Created by zyf on 2017/3/30.
 */
public class 兌水Decorator extends Decorator {


    public 兌水Decorator(飲品 yp) {
        super(yp);
    }

    public void 兌水(){ System.out.println("飲料兌水....尷尬不老鐵..."); } /*** * 那麼兌水後的價格應該是多少呢?<br/> * 應該是兌水的價格加飲品的價格 * @return 兌水2塊 */ @Override public int price() { return 2 + yp.price(); } /*** * 再複寫一個名字的方法<br/> * 現在已經不是單純的飲品了 * @return */ @Override public String getName() { 兌水(); return "兌水了的" + yp.getName(); } }

(7)測試類

public static void main(String[] args) {
  //可以看到,我們操作的引用一直是這個yp
  //但是這個引用指向的物件已經換了好幾次了
  //這就是為什麼裝飾類也要是飲品類的子類,因為只有這樣,裝飾類與被裝飾類才能被當做同一個型別使用(通過介面或繼承實現)
    飲品 yp = new 可樂Component();
    yp = new 兌水Decorator(yp);
    yp = new 加醋Decorator(yp);
//  上面與下面這一行是一樣的,是不是和IO流很像?
//  yp = new 加醋Decorator(new 兌水Decorator(new 可樂Component()));

    System.out.println("飲品名:" + yp.getName() + "---價格:" + yp.price()); }

note:我的理解是:

實際上裝飾者就先拿到被裝飾者的物件(比如飲品--可樂),這個物件可能有自己的一些方法可以被呼叫(比如getPrice);
我們建立裝飾者的物件,然後就可以給被裝飾者進行裝飾,比如重寫被裝飾者物件的方法(getPrice),因為頂級父類是一樣的(因為可樂和Decortor都繼承自飲品這個頂級父類),因此可以重寫。

 那對映到IO流中,比如FileInputStream作為被裝飾類,而BufferedInputStream作為具體的裝飾類(它繼承自FIlterInputStream類),同時FileInputStream和FilterInputStream都繼承自InputStream這個頂級父類。因此BufferedInputStream可以裝飾FileInputStream類,比如重寫了InputStream頂級父類的read方法,還添加了一些裝飾方法,比如mark和reset方法。

 

參考文獻:https://blog.csdn.net/android_zyf/article/details/68343953