設計模式學習之路 - 裝飾者模式 - 動態擴充器

來源:互聯網
上載者:User

今天瞭解下裝飾者模式。

首先,看下需求。

一家咖啡廳需要做一個訂單系統,為了配合他們的飲料供應需求。

首先有一個超類,飲料類。

package com.chris.decorator;public abstract class Beverage {String description = "Unkown Beverage";public String getDescription() {return description;}public abstract double cost();}

他有一個屬性,對飲料的描述,兩個方法,一個方法是返回描述,另一個方法是抽象方法, 返回飲料的價錢。


然後我們開始做事了,建立一個實體類,比如摩卡咖啡,繼承飲料類,然後加上相應的描述並且重寫cost()方法就行了。

每一種飲料都寫一個類去繼承,比如蒸餾咖啡, 黑咖啡, 拿鐵咖啡等等等等。。

好像看起來沒有什麼問題。


這時候,其他的需求就來了, 購買咖啡的時候,顧客可以要求向咖啡中加入各種調料, 比如蒸奶, 豆漿, 摩卡 或者奶泡等等。

然後我們開始不同的嘗試。


第一種嘗試:

按照上面的思路,我們對每一種不同的咖啡都建立一種新的類,去繼承超類,然後通過返回不同的描述和價格,達到我們的目的。

比如:黑咖啡, 加蒸奶的黑咖啡,加摩卡的黑咖啡, 加蒸奶和摩卡的黑咖啡。。。。。等等等等。。。

這時候我們會發現一個問題了,通過不同的組合,我們必須要窮舉所有的類型,才能把這個系統完善,這就要“類爆炸”了,現實中要維護這麼多同種類是十分可怕而且愚蠢的。


由於第一種方案很low,我們開始考慮其他的方案

第二種嘗試:

我們通過在超類中加屬性,蒸奶,摩卡等等。。然後返回是否需要蒸奶,是否需要摩卡等方法。。

這樣的話,通過設定不同的屬性,我們可以定義出不同的飲料,只需要設定是否需要蒸奶,是否需要摩卡等欄位就行了,再通過調料的選擇計算價格。

這個方案好像是比上面理智點,只需要基本的飲料類了, 至少不會由於調料的增加再增加新的類。


但是仔細想想, 好像還是有什麼不妥,如果再要加新的調料的話,我們還需要把這個超類屬性和相應的方法也修改。

這就違背了設計模式的一個重要的原則:類應該對擴充開發,對修改關閉。


第三種嘗試:

這裡,我們就開始引入我們的裝飾者模式。

為何叫裝飾者呢,就是因為我們只需要建一個飲料的對象,就可以用不同的調料去裝飾他,而且可以一層一層的裝飾,

最後調用cost()方法,通過依賴委託將調料的價格動態加上去。


如何理解呢,我們直接看代碼吧,前面的超類已經有了,我就不重複貼代碼了。

這裡,我們需要加一個新的裝飾者類---調料類,並且繼承飲料類,他只有一個抽象方法。

package com.chris.decorator;public abstract class Condiment extends Beverage{public abstract String getDescription();}

我們先實現一種咖啡, 濃縮咖啡。

package com.chris.decorator;public class Espresso extends Beverage{public Espresso() {description = "Espresso";}@Overridepublic double cost() {return 30;}}

然後我們再實現一種具體的調料,摩卡。

package com.chris.decorator;public class Mocha extends Condiment{Beverage beverage;public Mocha(Beverage beverage) {this.beverage = beverage;}@Overridepublic String getDescription() {return beverage.getDescription() + " add Mocha";}@Overridepublic double cost() {return beverage.cost() + 3;}}

調料的實現就比較關鍵了,在調料中,我們會有一個Beverage的屬性,當傳入某種飲料後,會預先處理這種飲料的方法,並返回當前飲料的最後價格。

這樣的話,就把調料裝飾了傳入的飲料, 並且可以一層一層的裝飾,並且返回最後的價格。

為了測試,我們再加入一種調料,奶泡。

package com.chris.decorator;public class Whip extends Condiment {Beverage beverage;public Whip(Beverage beverage) {this.beverage = beverage;}@Overridepublic String getDescription() {return beverage.getDescription() + " add Whip";}@Overridepublic double cost() {return beverage.cost() + 5;}}

下面,我們要點一杯濃縮咖啡,再點一杯加了摩卡和奶泡的濃縮咖啡。

package com.chris.decorator;public class CoffeeTestDrive {public static void main(String[] args) {Beverage beverage1 = new Espresso();System.out.println(beverage1.getDescription() + " ¥" + beverage1.cost());Beverage beverage2 = new Espresso();beverage2 = new Mocha(beverage2);beverage2 = new Whip(beverage2);System.out.println(beverage2.getDescription() + " ¥" + beverage2.cost());}}


再貼下測試結果咯, 錢應該算的清吧,算數應該沒這麼差吧。。

Espresso ¥30.0Espresso add Mocha add Whip ¥38.0


有木有很方便,一下讓代碼變的優雅起來。

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


使用裝飾者模式, 可以讓我們不需要改變原有的代碼就可以動態擴充新的需求.

在JDK的源碼中, FilterInputStream 和 BufferedInputStream 就是使用的裝飾著模式,通過一層一層的裝飾,將inputStream的功能豐富化.


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.