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