摘要:若要擴展功能,裝飾者提供了比繼承更有彈性的替代方案。裝飾者模式意味著一群裝飾者類,這些類用來包裝具體組件。裝飾者類反映出被裝飾組件類型。裝飾者會導(dǎo)致設(shè)計中出現(xiàn)許多小對象,如果過度使用,會讓程序變得很復(fù)雜。
嘿嘿嘿,你是不是很喜歡用繼承呢?感覺沒什么事情是一個“爸爸”類搞不定的,有的話就兩個,快來跟我看看這個模式吧,它能讓你“斷奶”,給愛用繼承的人一個全新的設(shè)計眼界。
直奔主題,你是否有聽說過組合呢?你是否覺得繼承往往有的時候沒你想象的那么好用呢?如果有,你又是否嘗試自己解決這個問題呢?讓我們來看個例子~~~~
假設(shè)我們有家咖啡店,設(shè)計如下
飲料(Beverage)基類,有4個子類,分別是綜合咖啡(HouseBlend),深焙咖啡 (DarkRoast),低咖啡因咖啡 (Decaf),(Espresso)濃縮咖啡,他們都是子類,繼承了cost支付方法。
嗯,看似目前沒啥問題,假設(shè)我需要改變點需求呢?
喝咖啡可以加各種各樣調(diào)料的,例如加牛奶,豆奶,摩卡,小餅干等等,而且每種飲料根據(jù)添加的調(diào)料不同還要再加對應(yīng)的調(diào)料費用,那么這些使用繼承的話。。。。。。恐怕會出現(xiàn)以下這樣的情況。。
想想以前這樣的設(shè)計,是不是有種細(xì)思極恐的感覺。。
當(dāng)然聰明一點的會這樣設(shè)計,這里我就不自己畫了,直接貼出來圖例
其實這樣的設(shè)計也非常有問題,跟上面"類爆炸"的設(shè)計沒啥區(qū)別,父類和子類依賴性太高了,低耦合。
1:假設(shè)我在父類中添加了新的調(diào)料,那么所有子類都需要相應(yīng)修改代碼,加大了工作量。
2:在父類中改變cost方法,子類也需要跟著改變。。
總之,太濫用繼承其實是給自己挖坑,繼承往往有的時候不能設(shè)計出彈性和易擴展性的代碼。這里就要說說和繼承的“死對頭”-->組合,而裝飾者模式就充分運用了組合
設(shè)計原則一:類應(yīng)該對擴展開放,對修改關(guān)閉。
注:這個原則剛開始聽是感覺自相矛盾的,確實剛開始我也有這疑問,回過頭看看觀察者模式,通過加入新的“觀察者”就相當(dāng)于拓展開放了“主題”,而“主題”本身沒有改變代碼,對修改關(guān)閉了。不急請往下看。
依舊以咖啡店做例子
這里改變做法,以飲料為主體,然后用調(diào)料“裝飾”飲料,
打個比方:如果顧客想要摩卡和奶泡深焙咖啡,要做的是:
1:拿一個深焙咖啡(HouseBlend)對象
2:以摩卡(Mocha)對象“裝飾”它
3:以奶泡(Whip)對象“裝飾”它
4:調(diào)用cost()方法,并依賴委托將調(diào)料價錢加上去
過程流程圖如下(請多看幾遍)
裝飾者模式定義:動態(tài)的將責(zé)任附加到對象上。若要擴展功能,裝飾者提供了比繼承更有彈性的替代方案。
放出裝飾者模型的類圖(重點看)
然后下面是基于此模式下的咖啡店的類圖
是不是感覺這個設(shè)計煥然一新。
下面我要好好打造我的咖啡店了
飲料抽象類
package Beverage; /** * 飲料抽象類 * 被裝飾者類 * @author Joy * */ public abstract class Beverage { //飲料名稱初始化 String description="未知飲料"; public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } //價錢方法抽象化,不需要具體實現(xiàn) public abstract double cost(); }
下面是四個咖啡類
package Beverage; /** * 深焙咖啡 * * @author Joy * */ public class DarkRoast extends Beverage { public DarkRoast() { description = "深焙咖啡"; } @Override public double cost() { return 0.99; } }
package Beverage; /** * 低咖啡因咖啡 * * * */ public class Decaf extends Beverage { public Decaf() { description = "低咖啡因咖啡"; } @Override public double cost() { return 1.05; } }
package Beverage; /** * * 濃縮咖啡類 * * */ //繼承飲料父類,因為它是飲料一種 public class Espresso extends Beverage { //寫一個構(gòu)造器,將飲料名稱重定義 public Espresso() { description="濃縮咖啡"; } //最后計算濃縮咖啡價格,先不算調(diào)料價錢,一杯濃縮咖啡1.99 @Override public double cost() { return 1.99; } }
package Beverage; /** * 綜合咖啡類 * * */ public class HouseBlend extends Beverage { public HouseBlend() { description = "綜合咖啡"; } @Override public double cost() { return 0.89; } }
調(diào)料抽象類
package Condiment; import Beverage.Beverage; /** * 調(diào)料抽象類 裝飾者類 * * @author Joy * */ public abstract class CondimentDecorator extends Beverage { // 描述 public abstract String getDescription(); }
下面是具體調(diào)料實現(xiàn)類
package Condiment; import Beverage.Beverage; /** * 摩卡類,裝飾者 * * @author Joy * */ // 摩卡是一個裝飾者,讓它繼承裝飾者父類 public class Mocha extends CondimentDecorator { // 設(shè)定飲料變量記錄飲料,也就是被裝飾者 Beverage beverage; // 設(shè)置構(gòu)造器將被裝飾者(飲料)被記錄到實例變量beverage中 // 把飲料設(shè)置構(gòu)造器參數(shù),再有構(gòu)造器將此記錄到實例變量中 public Mocha(Beverage beverage) { this.beverage = beverage; } // 重寫方法,將其飲料描述加上添加的調(diào)料 @Override public String getDescription() { return beverage.getDescription() + ",加摩卡"; } // 重寫方法,將飲料價錢+調(diào)料價錢 @Override public double cost() { return beverage.cost() + 0.2; } }
package Condiment; import Beverage.Beverage; /** * 豆?jié){類,裝飾者 * 內(nèi)容與前面摩卡一樣 * @author Joy * */ public class Soy extends CondimentDecorator { Beverage beverage; public Soy(Beverage beverage) { this.beverage=beverage; } @Override public String getDescription() { return beverage.getDescription()+",加豆?jié){"; } @Override public double cost() { return beverage.cost()+0.15; } }
package Condiment; import Beverage.Beverage; /** * 奶泡類,裝飾者 內(nèi)容與前面摩卡一樣 * * @author Joy * */ public class Whip extends CondimentDecorator { Beverage beverage; public Whip(Beverage beverage) { this.beverage = beverage; } @Override public String getDescription() { return beverage.getDescription() + ",加奶泡"; } @Override public double cost() { return beverage.cost() + 0.1; } }
測試類
package TestMain; import java.math.BigDecimal; import Beverage.Beverage; import Beverage.DarkRoast; import Beverage.Espresso; import Beverage.HouseBlend; import Condiment.Mocha; import Condiment.Soy; import Condiment.Whip; public class TestMain { public static void main(String[] args) { //要一杯濃縮咖啡,什么調(diào)料都不加 Beverage beverage = new Espresso(); System.out.println(beverage.getDescription() + " $" + beverage.cost()); //要一杯深焙咖啡 Beverage beverage2=new DarkRoast(); //加兩份摩卡 beverage2=new Mocha(beverage2); beverage2=new Mocha(beverage2); //加一份奶泡 beverage2=new Whip(beverage2); System.out.println(beverage2.getDescription()+" $"+beverage2.cost()); //要一杯加豆?jié){,加摩卡,加奶泡的綜合咖啡 Beverage beverage3=new HouseBlend(); beverage3=new Whip(beverage3); beverage3=new Mocha(beverage3); beverage3=new Soy(beverage3); //這里對價錢保留兩位小數(shù)四舍五入 double money=beverage3.cost(); BigDecimal b=new BigDecimal(money); double f1=b.setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue(); System.out.println(beverage3.getDescription()+" $"+f1); } }
效果圖:
要點:
1:繼承屬于擴展形式一種,但不見的是達(dá)到彈性設(shè)計的最佳方式,組合優(yōu)于繼承。
2:應(yīng)該允許行為可以被拓展,而無需修改現(xiàn)有的代碼。
3:裝飾者模式意味著一群裝飾者類,這些類用來包裝具體組件。
4:裝飾者類反映出被裝飾組件類型。
5:可以使用無數(shù)個裝飾者包裝一個組件。
6:裝飾者會導(dǎo)致設(shè)計中出現(xiàn)許多小對象,如果過度使用,會讓程序變得很復(fù)雜。
裝飾者模式結(jié)束,這個模式是個很有用的模式,可以說是提高代碼素養(yǎng)的關(guān)鍵一步,模擬代碼已經(jīng)全部放出,注釋也寫的比較清楚,若有不理解歡迎留言。
感謝你看到這里,裝飾者模式部分結(jié)束,本人文筆隨便,若有不足或錯誤之處望給予指點,90度彎腰~很快我會發(fā)布下一個設(shè)計模式內(nèi)容,生命不息,編程不止!
參考書籍:《Head First 設(shè)計模式》 參考網(wǎng)站: Java中的繼承與組合:http://www.importnew.com/12907.html
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/70184.html
摘要:下面總結(jié)了它倆的異同相同點都需要實現(xiàn)同一個接口或者繼承同一個抽象類,并且代理角色和裝飾角色都持有被代理角色和構(gòu)件角色的引用。 寫完上一篇之后有小伙伴問我有沒有寫過代理模式,想看看我的理解。原本我的設(shè)計模式系列是按照創(chuàng)建型-行為型-結(jié)構(gòu)型的順序?qū)懴氯サ模热恍』锇檎\心誠意了,我就大發(fā)慈悲的穿插一篇代理模式。開玩笑,題外話。 說起代理模式,就不由得想起經(jīng)紀(jì)人,說起經(jīng)紀(jì)人,就想起了...對,...
摘要:當(dāng)接口比較多,裝飾器也比較多時,可以獨立抽取一個裝飾器父類,實現(xiàn)目標(biāo)類的所有接口,再創(chuàng)建真正的裝飾器來繼承這個父類。四的實現(xiàn)方式提供了一種類似的注解的語法糖,來實現(xiàn)裝飾者模式。 歡迎關(guān)注我的公眾號睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言 所謂裝飾者模式,就是動態(tài)的給類或?qū)ο笤黾勇氊?zé)的設(shè)計模式。它...
摘要:裝飾者模式組成結(jié)構(gòu)抽象構(gòu)件給出抽象接口或抽象類,以規(guī)范準(zhǔn)備接收附加功能的對象。裝飾者模式圖解裝飾者模式應(yīng)用場景需要擴展一個類的功能,或給一個類添加附加職責(zé)。裝飾者對象接受所有來自客戶端的請求。參考資料設(shè)計模式 一、了解裝飾者模式 1.1 什么是裝飾者模式 裝飾者模式指的是在不必改變原類文件和使用繼承的情況下,動態(tài)地擴展一個對象的功能。它是通過創(chuàng)建一個包裝對象,也就是裝飾者來包裹真實的對...
摘要:我們今天也來做一個萬能遙控器設(shè)計模式適配器模式將一個類的接口轉(zhuǎn)換成客戶希望的另外一個接口。今天要介紹的仍然是創(chuàng)建型設(shè)計模式的一種建造者模式。設(shè)計模式的理論知識固然重要,但 計算機程序的思維邏輯 (54) - 剖析 Collections - 設(shè)計模式 上節(jié)我們提到,類 Collections 中大概有兩類功能,第一類是對容器接口對象進(jìn)行操作,第二類是返回一個容器接口對象,上節(jié)我們介紹了...
閱讀 2611·2023-04-25 15:07
閱讀 717·2021-11-24 10:21
閱讀 2322·2021-09-22 10:02
閱讀 3528·2019-08-30 15:43
閱讀 3240·2019-08-30 13:03
閱讀 2300·2019-08-29 17:18
閱讀 3598·2019-08-29 17:07
閱讀 1886·2019-08-29 12:27