摘要:裝飾者模式組成結(jié)構(gòu)抽象構(gòu)件給出抽象接口或抽象類,以規(guī)范準(zhǔn)備接收附加功能的對象。裝飾者模式圖解裝飾者模式應(yīng)用場景需要擴展一個類的功能,或給一個類添加附加職責(zé)。裝飾者對象接受所有來自客戶端的請求。參考資料設(shè)計模式
一、了解裝飾者模式
1.1 什么是裝飾者模式
裝飾者模式指的是在不必改變原類文件和使用繼承的情況下,動態(tài)地擴展一個對象的功能。它是通過創(chuàng)建一個包裝對象,也就是裝飾者來包裹真實的對象。
所以裝飾者可以動態(tài)地將責(zé)任附加到對象上。若要擴展功能,裝飾者提供了比繼承更有彈性的方案。
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)用場景
需要擴展一個類的功能,或給一個類添加附加職責(zé)。
需要動態(tài)的給一個對象添加功能,這些功能可以再動態(tài)的撤銷。
需要增加由一些基本功能的排列組合而產(chǎn)生的非常大量的功能,從而使繼承關(guān)系變的不現(xiàn)實。
當(dāng)不能采用生成子類的方法進(jìn)行擴充時。可能有大量獨立的擴展,為支持每一種組合將產(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)該對擴展開放,對修改關(guān)閉。
在設(shè)計過程中,我們允許類容易擴展,在不修改原有代碼的情況下,就可以擴展新的行為。這樣的設(shè)計具有彈性可以應(yīng)對改變,可以接受新的功能來應(yīng)對新的需求。
將裝飾者模式應(yīng)用到問題中去:假如我們想要摩卡和豆?jié){深焙咖啡,那么,要做的是:
拿一個深焙咖啡 (DarkRoast) 對象
以摩卡 (Mocha) 裝飾它
以豆?jié){ (Soy) 裝飾它
調(diào)用 cost() 方法,并依賴委托將調(diào)料的價錢加上去
(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 "; } @Override public double cost() { return 1.99; } }
黑咖啡 HouseBlend 類 (具體構(gòu)件)
package com.jas.decorator; public class HouseBlend extends Beverage { public HouseBlend(){ description = "House Blend Coffee "; } @Override public double cost() { return 0.80; } }
調(diào)料 CondimentDecorator 抽象類 (抽象裝飾構(gòu)件)
package com.jas.decorator; public abstract class CondimentDecorator extends Beverage{ @Override public 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ù)將被裝飾者實例化 } @Override public String getDescription() { return beverage.getDescription() + ", Mocha "; //用來加上調(diào)料,一起描述飲料 } @Override public 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; } @Override public String getDescription() { return beverage.getDescription() + ", Soy "; } @Override public 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/O3.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; /** * 擴展 FilterInputStream,這是所有 InputStream 的抽象裝飾者 */ public class LowerCaseInputStream extends FilterInputStream { public LowerCaseInputStream(InputStream inputStream){ super(inputStream); } @Override public int read() throws IOException{ int c = super.read(); return (c == -1 ? c : Character.toLowerCase((char)c)); } @Override public 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)點
Decorator 模式與繼承關(guān)系的目的都是要擴展對象的功能,但是 Decorator 可以提供比繼承更多的靈活性。
通過使用不同的具體裝飾類以及這些裝飾類的排列組合,設(shè)計者可以創(chuàng)造出很多不同行為的組合。
缺點
這種比繼承更加靈活機動的特性,也同時意味著更加多的復(fù)雜性。
裝飾模式會導(dǎo)致設(shè)計中出現(xiàn)許多小類 (I/O 類中就是這樣),如果過度使用,會使程序變得很復(fù)雜。
裝飾模式是針對抽象組件(Component)類型編程。但是,如果你要針對具體組件編程時,就應(yīng)該重新思考你的應(yīng)用架構(gòu),以及裝飾者是否合適。
參考資料《Head First 設(shè)計模式》
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/77260.html
摘要:相關(guān)設(shè)計模式裝飾者模式和代理模式裝飾者模式關(guān)注再一個對象上動態(tài)添加方法代理模式關(guān)注再對代理對象的控制訪問,可以對客戶隱藏被代理類的信息裝飾著模式和適配器模式都叫包裝模式關(guān)于新職責(zé)適配器也可以在轉(zhuǎn)換時增加新的職責(zé),但主要目的不在此。 0x01.定義與類型 定義:裝飾模式指的是在不必改變原類文件和使用繼承的情況下,動態(tài)地擴展一個對象的功能。它是通過創(chuàng)建一個包裝對象,也就是裝飾來包裹真實的...
摘要:裝飾者模式是動態(tài)地將責(zé)任附加到對象上。然后我們在子類計算價格的時候加上父類中計算好的配料的價格。結(jié)果可樂加冰可樂加冰加糖在的類庫中就有很多實際應(yīng)用到了裝飾模式,比如就可以用來裝飾,提供更加強大的功能。 裝飾者模式是動態(tài)地將責(zé)任附加到對象上。若要擴展功能,裝飾者提供了比繼承更有彈性的替代方案。 假設(shè)我們有一個需求,是給一家飲料店做一個計算各種飲料價格的功能。聽起來很簡單,我們創(chuàng)建一個抽象...
摘要:測試類設(shè)計模式裝飾者模式工廠模式只能讀啦會報錯只讀異常可以正確運行第二部分定義抽象組件是具體組件和抽象裝飾類的共同父類,聲明了在具體組件中實現(xiàn)的方法。 前言 本篇文章分為四個部分:第一部分會舉一個例子引出裝飾者模式,讓讀者對裝飾者模式有個感官上的認(rèn)識;第二部分會給出裝飾者模式的定義(當(dāng)然我們主要不是來背定義,就當(dāng)做積累專業(yè)名詞來記吧,我個人是很不喜歡下定義的);第三部分,我會拿jdk中...
摘要:若要擴展功能,裝飾者提供了比繼承更有彈性的替代方案。裝飾者模式意味著一群裝飾者類,這些類用來包裝具體組件。裝飾者類反映出被裝飾組件類型。裝飾者會導(dǎo)致設(shè)計中出現(xiàn)許多小對象,如果過度使用,會讓程序變得很復(fù)雜。 嘿嘿嘿,你是不是很喜歡用繼承呢?感覺沒什么事情是一個爸爸類搞不定的,有的話就兩個,快來跟我看看這個模式吧,它能讓你斷奶,給愛用繼承的人一個全新的設(shè)計眼界。 直奔主題,你是否有聽說...
摘要:適配器模式不應(yīng)在設(shè)計階段考慮,它是為了解決已經(jīng)上線的問題的存在。組合模式將對象組合成樹形結(jié)構(gòu)以表示部分整體的層次結(jié)構(gòu),使得用戶對單個對象和組合對象的使用具有一致性。 代理模式 代理模式之前已經(jīng)講過,附上鏈接代理模式 裝飾者模式 裝飾者模式定義:動態(tài)地給一個對象添加一些額外的職責(zé)。就增加功能來說,裝飾模式相比生成子類更為靈活。 裝飾模式博主在第一次學(xué)習(xí)是懵逼的,是因為代理模式中代理對象和...
閱讀 3707·2021-10-18 13:34
閱讀 2396·2021-08-11 11:15
閱讀 1201·2019-08-30 15:44
閱讀 687·2019-08-26 10:32
閱讀 985·2019-08-26 10:13
閱讀 2064·2019-08-23 18:36
閱讀 1774·2019-08-23 18:35
閱讀 523·2019-08-23 17:10