Java 设计模式之装饰者模式
一、了解裝飾者模式
1.1 什么是裝飾者模式
裝飾者模式指的是在不必改變原類文件和使用繼承的情況下,動態(tài)地擴(kuò)展一個對象的功能。它是通過創(chuàng)建一個包裝對象,也就是裝飾者來包裹真實的對象。
所以裝飾者可以動態(tài)地將責(zé)任附加到對象上。若要擴(kuò)展功能,裝飾者提供了比繼承更有彈性的方案。
1.2 裝飾者模式組成結(jié)構(gòu)
- 抽象構(gòu)件 (Component):給出抽象接口或抽象類,以規(guī)范準(zhǔn)備接收附加功能的對象。
- 具體構(gòu)件 (ConcreteComponent):定義將要接收附加功能的類。
- 抽象裝飾 (Decorator):裝飾者共同要實現(xiàn)的接口,也可以是抽象類。
- 具體裝飾 (ConcreteDecorator):持有一個 Component 對象,負(fù)責(zé)給構(gòu)件對象“貼上”附加的功能。
1.3 裝飾者模式 UML 圖解
1.4 裝飾者模式應(yīng)用場景
- 需要擴(kuò)展一個類的功能,或給一個類添加附加職責(zé)。
- 需要動態(tài)的給一個對象添加功能,這些功能可以再動態(tài)的撤銷。
- 需要增加由一些基本功能的排列組合而產(chǎn)生的非常大量的功能,從而使繼承關(guān)系變的不現(xiàn)實。
- 當(dāng)不能采用生成子類的方法進(jìn)行擴(kuò)充時。可能有大量獨立的擴(kuò)展,為支持每一種組合將產(chǎn)生大量的子類,使得子類數(shù)目呈爆炸性增長。
1.5 裝飾者模式特點
- 裝飾者對象和具體構(gòu)件有相同的接口。這樣客戶端對象就能以和真實對象相同的方式和裝飾對象交互。
- 裝飾者對象包含一個具體構(gòu)件的引用(reference)。
- 裝飾者對象接受所有來自客戶端的請求。它把這些請求轉(zhuǎn)發(fā)給具體構(gòu)件。
- 裝飾者對象可以在轉(zhuǎn)發(fā)這些請求以前或以后動態(tài)增加一些功能。
二、裝飾者模式具體應(yīng)用
2.1 問題描述
星巴茲咖啡訂單系統(tǒng):星巴茲店提供了各式各樣的咖啡,以及各種咖啡調(diào)料。為了適應(yīng)飲料需求供應(yīng),所以讓你設(shè)計一個更新訂單系統(tǒng)。
2.2 使用繼承
在購買咖啡時,可以要求在咖啡中添加各種調(diào)料,例如:豆?jié){ (Soy)、摩卡 (Mocha)、奶泡等。由于各種調(diào)料的價格不相同,所以訂單系統(tǒng)必須要考慮這些因素。于是就有了下面的嘗試
2.3 繼承類圖
這還只是列出了一部分的類,這簡直是類爆炸,可以看出這樣做顯然是不行的。所以我們要慎用繼承,盡量用組合和委托。
2.4 裝飾者模式登場
裝飾者模式涉及到的一個重要的設(shè)計原則 (當(dāng)然還涉及到了其他的設(shè)計原則,比如多用組合,少用繼承等):類應(yīng)該對擴(kuò)展開放,對修改關(guān)閉。
在設(shè)計過程中,我們允許類容易擴(kuò)展,在不修改原有代碼的情況下,就可以擴(kuò)展新的行為。這樣的設(shè)計具有彈性可以應(yīng)對改變,可以接受新的功能來應(yīng)對新的需求。
將裝飾者模式應(yīng)用到問題中去:假如我們想要摩卡和豆?jié){深焙咖啡,那么,要做的是:
(1) 裝飾者模式設(shè)計圖
(2)代碼實現(xiàn)
飲料 Beverage 抽象類 (抽象構(gòu)件)
package com.jas.decorator;public abstract class Beverage {String description = "Unknown Beverage";public String getDescription() {return description;}public abstract double cost(); }濃咖啡 Espresso 類 (具體構(gòu)件)
package com.jas.decorator;public class Espresso extends Beverage {public Espresso(){description = "Espresso ";}@Overridepublic double cost() {return 1.99;} }黑咖啡 HouseBlend 類 (具體構(gòu)件)
package com.jas.decorator;public class HouseBlend extends Beverage {public HouseBlend(){description = "House Blend Coffee ";}@Overridepublic double cost() {return 0.80;} }調(diào)料 CondimentDecorator 抽象類 (抽象裝飾構(gòu)件)
package com.jas.decorator;public abstract class CondimentDecorator extends Beverage{@Overridepublic abstract String getDescription(); }摩卡 Mocha 類 (具體裝飾構(gòu)件)
package com.jas.decorator;public class Mocha extends CondimentDecorator {private Beverage beverage = null; //用一個實例變量來記錄飲料,也就是被裝飾者public Mocha(Beverage beverage){this.beverage = beverage; //通過構(gòu)造函數(shù)將被裝飾者實例化}@Overridepublic String getDescription() {return beverage.getDescription() + ", Mocha "; //用來加上調(diào)料,一起描述飲料}@Overridepublic double cost() {return 0.2 + beverage.cost(); //計算摩卡飲料的價錢,為摩卡價錢 + 飲料價錢} }豆?jié){ Soy 類 (具體裝飾構(gòu)件)
package com.jas.decorator;public class Soy extends CondimentDecorator {private Beverage beverage = null;public Soy(Beverage beverage){this.beverage = beverage;}@Overridepublic String getDescription() {return beverage.getDescription() + ", Soy ";}@Overridepublic double cost() {return 0.1 + beverage.cost();} }測試代碼 StarbuzzCoffee 類
package com.jas.decorator;public class StarbuzzCoffee {public static void main(String[] args) {//簡單要一杯濃咖啡Beverage beverage1 = new Espresso();System.out.println(beverage1.getDescription() + "$" + beverage1.cost());//兩份摩卡加一份豆?jié){的濃咖啡Beverage beverage2 = new Espresso();beverage2 = new Mocha(beverage2);beverage2 = new Mocha(beverage2);beverage2 = new Soy(beverage2);System.out.println(beverage2.getDescription() + "$" + beverage2.cost());//一份摩卡加一份豆?jié){的黑咖啡Beverage beverage3 = new HouseBlend();beverage3 = new Mocha(beverage3);beverage3 = new Soy(beverage3);System.out.println(beverage3.getDescription() + "$" + beverage3.cost());} }/*** 輸出* Espresso $1.99* Espresso , Mocha , Mocha , Soy $2.49* House Blend Coffee , Mocha , Soy $1.1*/2.5 裝飾者模式問題總結(jié)
- 裝飾者與被裝飾對象有相同的超類型 (DarkRoast 與裝飾類 Mocha 和 Soy 都繼承自 Beverage(飲料))。
- 可以使用一個或多個裝飾對象包裝一個對象。
- 因為裝飾者與被裝飾者具有相同的超類型,所以在任何需要原始對象的情況下,都可以用裝飾過的對象去代替它。
- 裝飾者可以在所委托被裝飾者的行為之前與之后,加上自己的行為,以達(dá)到特定的目的。
- 對象可以在任何時候被裝飾,所以在運行時動態(tài)地、不限量地用你喜歡的裝飾者去裝飾對象。
三、真實世界的裝飾者 Java I/O
3.1 了解 Java I/O 裝飾者模式
在了解了裝飾者模式之后,I/O 相關(guān)的類對你來說就更有意義了,因為這其中很多類都是裝飾者。比如下面相關(guān)的類
裝飾 I/O 類
3.2 自定義 Java I/O 裝飾者
問題描述:讀取文件,把輸入流內(nèi)的所有大寫字符轉(zhuǎn)為小寫。
裝飾者 LowerCaseInputStream 類
package com.jas.decorator;import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream;/*** 擴(kuò)展 FilterInputStream,這是所有 InputStream 的抽象裝飾者*/ public class LowerCaseInputStream extends FilterInputStream {public LowerCaseInputStream(InputStream inputStream){super(inputStream);}@Overridepublic int read() throws IOException{int c = super.read();return (c == -1 ? c : Character.toLowerCase((char)c));}@Overridepublic int read(byte[] bytes, int offset, int len) throws IOException{int result = super.read(bytes, offset, len);for (int i = offset; i < offset + result; i++) {bytes[i] = (byte) Character.toLowerCase((char)bytes[i]);}return result;} }測試 InputTest 類
package com.jas.decorator;import java.io.*;public class InputTest {public static void main(String[] args) {int c = 0;InputStream in = null;try {//設(shè)置 FileInputStream ,先用 BufferedInputStream 裝飾它,再用 LowerCaseInputStream 進(jìn)行裝飾in = new LowerCaseInputStream(new BufferedInputStream(new FileInputStream("test.txt")));while ((c = in.read()) >= 0){System.out.print((char)c);}in.close();} catch (IOException e) {e.printStackTrace();}} }/**在文件中為“HELLO WORLD”** 輸出* hello world*/四、裝飾者模式總結(jié)
4.1 裝飾者模式的優(yōu)缺點
優(yōu)點
缺點
PS:點擊了解更多設(shè)計模式 http://blog.csdn.net/codejas/article/details/79236013
參考文獻(xiàn)
《Head First 設(shè)計模式》
總結(jié)
以上是生活随笔為你收集整理的Java 设计模式之装饰者模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java 设计模式之观察者模式
- 下一篇: 吸食上头电子烟会被拘留多久