1. 程式人生 > >Java設計模式の裝飾者模式

Java設計模式の裝飾者模式

解決 over 裝飾者模式 pack img 應該 ora 我們 lan

目錄


  

  一、問題引入
  二、設計原則
  三、用裝飾者模式解決問題
  四、裝飾者模式的特點
  五、裝飾者模式的定義
  六、裝飾者模式的實現
  七、java.io包內的裝飾者模式


一、問題引入

  咖啡店的類設計:

  一個飲料基類,各種飲料類繼承這個基類,並且計算各自的價錢。

  飲料中需要加入各種調料,考慮在基類中加入一些布爾值變量代表是否加入各種調料,基類的cost()中的計算各種調料的價錢,子類覆蓋cost(),並且在其中調用超類的cost(),加上特定飲料的價錢,計算出子類特定飲料的價錢。

  缺點:類數量爆炸、基類加入的新功能並不適用於所有的子類、調料價錢的改變、新調料的出現都會要求改變現有代碼;有的子類並不適合某些調料等情況……

二、設計原則

  類應該對擴展開放,對修改關閉。

  我們的目標是允許類容易擴展,在不修改現有代碼的情況下,就可搭配新的行為。

  如能實現這樣的目標,有什麽好處呢?這樣的設計具有彈性可以應對改變,可以接受新的功能來應對改變的需求。

  要讓OO設計同時具備開放性和關閉性,不是一件容易的事,通常來說,沒有必要把設計的每個部分都這麽設計。

  遵循開放-關閉原則,通常會引入新的抽象層次,增加代碼的復雜度。

  我們需要把註意力集中在設計中最有可能改變的地方,然後應用開放-關閉原則。

三、用裝飾者模式解決問題

  解決咖啡店飲料問題的方法:

  以飲料為主體,然後在運行時以調料來“裝飾”飲料。

  比如,顧客想要摩卡(Mocha)和奶泡(Whip)深焙咖啡(DarkRoast):

  DarkRoast繼承自Beverage,有一個cost()方法。

  第一步,以DarkRoast對象開始;

  第二步,顧客想要摩卡,所以建立一個Mocha裝飾者對象,並用它將DarkRoast對象包裝(wrap)起來;

  第三步,顧客想要奶泡,所以建立一個Whip裝飾者對象,並用它將Mocha對象包起來;(Mocha和Whip也繼承自Beverage,有一個cost()方法);

  最後,為顧客算錢,通過調用最外圈裝飾者(Whip)的cost()就可以。Whip()的cost()會先委托它裝飾的對象(Mocha)計算出價錢,然後在加上奶泡的價錢。Mocha的cost()也是類似。

四、裝飾者模式的特點

  裝飾者和被裝飾對象有相同的超類型

  可以用一個或多個裝飾者包裝一個對象。

  因為裝飾者和被裝飾者具有相同的類型,所以任何需要原始對象的場合,可以用裝飾過的對象代替。

  裝飾者可以在所委托被裝飾者的行為之前與/或之後,加上自己的行為,以達到特定的目的。

  對象可以在任何時候被裝飾,所以可以在運行時動態地、不限量地用你喜歡的裝飾者來裝飾對象。

五、裝飾者模式的定義

  裝飾者模式動態地將責任附加到對象上。若要擴展功能,裝飾者提供了比繼承更有彈性的替代方案。

六、裝飾者模式的實現

  實現類圖如下:

技術分享

裝飾者和被裝飾者具有共同的超類,利用繼承達到“類型匹配”,而不是利用繼承獲得“行為”;將裝飾者和被裝飾者組合時,加入新的行為。

詳細源碼:

package com.blankjor.decorator;

/**
 * @desc 抽象飲料類(抽象組件)
 * @author Blankjor
 * @date 2017年5月14日 上午11:14:06
 */
public abstract class Beverage {
    String description = "Unkown Beverage";

    public String getDescription() {
        return description;
    }

    /**
     * 抽象價格計算方法
     * 
     * @return
     */
    public abstract double cost();
}

package com.blankjor.decorator;

/**
 * @desc 濃縮飲料 (具體組件)
 * @author Blankjor
 * @date 2017年5月14日 上午11:20:11
 */
public class Espresso extends Beverage {
    public Espresso() {
        description = "Espresso";
    }

    @Override
    public double cost() {
        return 1.99;
    }

}

package com.blankjor.decorator;

/**
 * @desc House Blend 飲料(具體組件)
 * @author Blankjor
 * @date 2017年5月14日 上午11:20:11
 */
public class HouseBlend extends Beverage {
    public HouseBlend() {
        description = "House Blend";
    }

    @Override
    public double cost() {
        return .20;
    }

}

package com.blankjor.decorator;

/**
 * @desc 抽象裝飾者類
 * @author Blankjor
 * @date 2017年5月14日 上午11:17:12
 */
public abstract class CondimentDecorator extends Beverage {
    /**
     * 為了後面的調料都能夠獲取到自己調料的描述
     */
    public abstract String getDescription();

}


package com.blankjor.decorator;

/**
 * @desc Mocha調料(具體裝飾者)
 * @author Blankjor
 * @date 2017年5月14日 上午11:23:36
 */
public class Mocha extends CondimentDecorator {
    Beverage beverage;

    public Mocha(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ",Mocha";
    }

    @Override
    public double cost() {
        return .20 + beverage.cost();
    }
}

package com.blankjor.decorator;

/**
 * @desc Soy調料(具體裝飾者)
 * @author Blankjor
 * @date 2017年5月14日 上午11:20:11
 */
public class Soy extends CondimentDecorator {
    Beverage beverage;

    public Soy(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ",Soy";
    }

    @Override
    public double cost() {
        return .60 + beverage.cost();
    }
}

package com.blankjor.decorator;

/**
 * @desc Whip調料(具體裝飾者)
 * @author Blankjor
 * @date 2017年5月14日 上午11:20:11
 */
public class Whip extends CondimentDecorator {
    Beverage beverage;

    public Whip(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ",Whip";
    }

    @Override
    public double cost() {
        return .40 + beverage.cost();
    }
}

package com.blankjor.decorator;

/**
 * @desc 測試裝飾者模式
 * @author Blankjor
 * @date 2017年5月14日 上午11:29:50
 */
public class MainTest {
    public static void main(String[] args) {
        // 創建一種調料
        Beverage beverage = new Espresso();
        // 描述和價格
        System.out.println(beverage.getDescription() + " $" + beverage.cost());
        Beverage beverage1 = new HouseBlend();

        beverage1 = new Mocha(beverage1);
        beverage1 = new Whip(beverage1);
        beverage1 = new Soy(beverage1);
        System.out.println(beverage1.getDescription() + " $" + beverage1.cost());
        Beverage beverage2 = new Espresso();

        beverage2 = new Mocha(beverage2);
        beverage2 = new Whip(beverage2);
        beverage2 = new Soy(beverage2);
        beverage2 = new Mocha(beverage2);
        System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
    }

}

運行結果:

技術分享

裝飾者和被裝飾者具有共同的超類,利用繼承達到“類型匹配”,而不是利用繼承獲得“行為”;將裝飾者和被裝飾者組合時,加入新的行為。

  解決本文中飲料的具體問題時,圖中Component即為Beverage(可以是抽象類或者接口),而ConcreteComponent為各種飲料,Decorator(抽象裝飾者)為調料的抽象類或接口,ConcreteDecoratorX則為各種具體的調料。

  因為使用對象組合,可以把飲料和調料更有彈性地加以混合與匹配。

  代碼外部細節:

  代碼中實現的時候,通過構造函數將被裝飾者傳入裝飾者中即可,如最後的調用形式如下:

    Beverage beverage = new DarkRoast();

    beverage = new Mocha(beverage);

    beverage = new Whip(beverage);

  即完成了兩層包裝,此時再調用beverage的cost()函數即可得到總價。

七、java.io包內的裝飾者模式

技術分享

裝飾者模式的缺點:在設計中加入大量的小類,如果過度使用,會讓程序變得復雜。

參考:http://www.cnblogs.com/mengdd/archive/2013/01/03/2843439.html

Java設計模式の裝飾者模式