摘要:裝飾者模式是動態地將責任附加到對象上。然后我們在子類計算價格的時候加上父類中計算好的配料的價格。結果可樂加冰可樂加冰加糖在的類庫中就有很多實際應用到了裝飾模式,比如就可以用來裝飾,提供更加強大的功能。
裝飾者模式是動態地將責任附加到對象上。若要擴展功能,裝飾者提供了比繼承更有彈性的替代方案。
假設我們有一個需求,是給一家飲料店做一個計算各種飲料價格的功能。聽起來很簡單,我們創建一個抽象父類Beverages,description用來描述飲料名字,price方法用來計算飲料的價格。
public abstract class Beverages { private String description; public String getDescription() { return description; } public abstract double price(); }
每種飲料我們都創建一個子類去繼承父類,賦值給description,并且重寫price方法來設置自己的價格。看上去很完美,我們運行一下看看結果。
public class Coffee extends Beverages { public Coffee(String description){ this.description = description; } @Override public String getDescription() { return description; } @Override public double price() { return 3.5; } } public static void main(String[] args){ Coffee coffee = new Coffee("咖啡"); System.out.println(coffee.getDescription()); System.out.println(coffee.price()); }
結果:
咖啡
3.5
但是問題來了,飲料店里不僅僅只有一種飲料,還有可樂、七喜、奶茶其他各種各樣的飲料,難道我們要每個都創建一個子類嗎?好吧,就算你覺得幾十種飲料還不算多,那如果各種飲料直接再進行搭配呢?咖啡加牛奶、咖啡加巧克力、加糖、不加糖,因為各種配料的不同價格和描述也不同,我的天呀,難道要創建幾百個類嗎?這個時候我們突然想到了不是可以用繼承來解決這個問題嗎?那我們就來試一下。
我們改造一下Beverages類,把要加的配料用boolean值聲明,如果需要添加配料就調用set方法設置為true,在price方法中計算的時候來判斷哪一些配料添加了需要計算價格。
public class Beverages { public String description; public boolean milk; public boolean sugar; public double milkPrice = 1.5; public double sugarPrice = 0.5; public boolean isMilk() { return milk; } public void setMilk(boolean milk) { this.milk = milk; } public boolean isSugar() { return sugar; } public void setSugar(boolean sugar) { this.sugar = sugar; } public String getDescription() { return description; } public double price(){ double basePrice = 0.0; if (isMilk()){ basePrice += milkPrice; } if (isSugar()){ basePrice += sugarPrice; } return basePrice; } }
然后我們在子類計算價格的時候加上父類中計算好的配料的價格。
public class CoffeeWithMilk extends Beverages { public CoffeeWithMilk(String description){ this.description = description; } @Override public double price() { return 3.0 + super.price(); } }
我們運行看一下,咖啡加牛奶加糖,結果沒有問題。
CoffeeWithMilk coffeeWithMilk = new CoffeeWithMilk("咖啡加牛奶加糖"); coffeeWithMilk.setMilk(true); coffeeWithMilk.setSugar(true); System.out.println(coffeeWithMilk.getDescription()); System.out.println(coffeeWithMilk.price());
結果:
咖啡加牛奶加糖
5.0
這樣一來我們解決了新建無數個子類的問題,但是我們發現單純繼承的做法還是有太多弊端,比如說如果我們想要新添加配料,我們得修改在父類Beverages中添加新的調料字段,還要修改price方法,這嚴重違反了開發-關閉的設計原則,類應該對擴展開發,對修改關閉。而且有些飲料和配料是沒辦法搭配的,例如啤酒加糖,但是子類還是會繼承到這些配料,并且如果是要同一份配料要加雙份又該怎么改呢?所以單純繼承的方法還是不行。這時候我們就要使用到裝飾者模式。
首先我們創建抽象父類Beverages,這個父類只是飲料的父類,不是配料的父類。我們創建一個Cola類直接繼承它。
public abstract class Beverages { public String description; public String getDescription() { return description; } public abstract double price(); } public class Cola extends Beverages { public Cola(String description) { this.description = description; } @Override public double price() { return 2.0; } }
現在就缺配料的部分的代碼了,我們再創建一個配料的抽象類Seasonings,我們讓它繼承Berverages類,并且聲明了一個Berverages的引用和抽象的getDescription方法,這里我們稍后再作解釋。
public abstract class Seasonings extends Beverages{ public Beverages beverages; public abstract String getDescription(); }
接下來我們看看配料的實現類怎么寫。我們讓他繼承了父類Seasonings,構造函數接收父類Beverages類型,其實也就是要被裝飾的對象,也就是各種各樣需要加配料的飲料。然后我們重寫了getDescription,我們在傳進來的那個Beverages對象的基礎上添加名字,price方法也是同理。這樣其實就在給傳進來的對象外面做了一層裝飾,也就是給飲料添加了配料。
public class Ice extends Seasonings { public Ice(Beverages beverages) { this.beverages = beverages; } @Override public String getDescription() { return beverages.getDescription() + "加冰"; } @Override public double price() { return beverages.price() + 0.2; } }
運行一下,看看結果。沒有問題,可以搭配成功。
Cola cola = new Cola("可樂"); Beverages ice = new Ice(cola); System.out.println(ice.getDescription()); System.out.println(ice.price());
結果:
可樂加冰
2.2
我們還想再添加別的配料,再創建一個新的配料。
public class Sugar extends Seasonings { public Sugar(Beverages beverages) { this.beverages = beverages; } @Override public String getDescription() { return beverages.getDescription() + "加糖"; } @Override public double price() { return 0.5 + beverages.price(); } }
我們在原來搭配好的基礎上再進行裝飾,結果也是沒有問題的。這個時候就要提到為什么我們的Seasonings類要繼承Beverages類了,因為每個配料的實現類里有一個Beverages類型的引用,這樣我們才可以對它的子類進行裝飾,我們讓Seasonings也繼承Beverages那它的子類也是Beverages類型,我們就可以想裝飾幾層就裝飾幾層。
Beverages ice = new Ice(cola); System.out.println(ice.getDescription()); System.out.println(ice.price()); Beverages sugar = new Sugar(ice); System.out.println(sugar.getDescription()); System.out.println(sugar.price());
結果:
可樂加冰
2.2
可樂加冰加糖
2.7
在Java的類庫中就有很多實際應用到了裝飾模式,比如BufferedInputStream就可以用來裝飾FileInputStream,提供更加強大的功能。
總結:
裝飾模式就是裝飾者和被裝飾者都具有相同的超類,裝飾者拿到被裝飾者的引用之后,在調用被裝飾者的方法的同時再加上自己的新功能,從而實現了功能的增加,也不需要修改原來的代碼。而且因為是相同的超類,所以可以裝飾很多層。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/69053.html
摘要:裝飾者模式組成結構抽象構件給出抽象接口或抽象類,以規范準備接收附加功能的對象。裝飾者模式圖解裝飾者模式應用場景需要擴展一個類的功能,或給一個類添加附加職責。裝飾者對象接受所有來自客戶端的請求。參考資料設計模式 一、了解裝飾者模式 1.1 什么是裝飾者模式 裝飾者模式指的是在不必改變原類文件和使用繼承的情況下,動態地擴展一個對象的功能。它是通過創建一個包裝對象,也就是裝飾者來包裹真實的對...
摘要:于是乎,老李家去車行把車進行了改裝,車速上去了,媳婦也領到了,皆大歡喜這其實就是一個裝飾器的使用案例,原來的車已經沒法滿足了,所以進行了速度增強,其他功能并沒有改變。 裝飾器設計模式的概念 在實際生產中,某個類的行為(它所提供的方法)已經沒法滿足當前的需要了,但是又需要使用原有的部分功能,因此需要對原有對象進行增強——裝飾器設計模式(Decorator Pattern)也叫包裝器模式就...
摘要:哪吒社區技能樹打卡打卡貼函數式接口簡介領域優質創作者哪吒公眾號作者架構師奮斗者掃描主頁左側二維碼,加入群聊,一起學習一起進步歡迎點贊收藏留言前情提要無意間聽到領導們的談話,現在公司的現狀是碼農太多,但能獨立帶隊的人太少,簡而言之,不缺干 ? 哪吒社區Java技能樹打卡?【打卡貼 day2...
摘要:讓你收獲滿滿碼個蛋從年月日推送第篇文章一年過去了已累積推文近篇文章,本文為年度精選,共計篇,按照類別整理便于讀者主題閱讀。本篇文章是今年的最后一篇技術文章,為了讓大家在家也能好好學習,特此花了幾個小時整理了這些文章。 showImg(https://segmentfault.com/img/remote/1460000013241596); 讓你收獲滿滿! 碼個蛋從2017年02月20...
摘要:相關設計模式裝飾者模式和代理模式裝飾者模式關注再一個對象上動態添加方法代理模式關注再對代理對象的控制訪問,可以對客戶隱藏被代理類的信息裝飾著模式和適配器模式都叫包裝模式關于新職責適配器也可以在轉換時增加新的職責,但主要目的不在此。 0x01.定義與類型 定義:裝飾模式指的是在不必改變原類文件和使用繼承的情況下,動態地擴展一個對象的功能。它是通過創建一個包裝對象,也就是裝飾來包裹真實的...
閱讀 497·2021-09-03 00:22
閱讀 1372·2021-08-03 14:03
閱讀 2088·2021-07-25 21:37
閱讀 653·2019-08-30 13:18
閱讀 1880·2019-08-29 16:19
閱讀 2689·2019-08-29 13:22
閱讀 1299·2019-08-29 12:16
閱讀 2590·2019-08-26 12:16