JAVA設計模式之裝飾者模式

来源:https://www.cnblogs.com/bearduncle/archive/2017/12/25/8110253.html
-Advertisement-
Play Games

咖啡店需要做一個訂單系統,以合乎飲料供應要求。 1.最初是這樣設計的: 每一種飲料都需要繼承該抽象類,並覆寫cost()方法。 2.但是購買咖啡時需要考慮到調料的部分,每種咖啡會加不同種的調料,比如蒸奶、豆漿、摩卡或者覆蓋奶泡,那麼訂單系統需要考慮加入不同調料後的價格。因此需要實現不同的子類來定義添 ...


咖啡店需要做一個訂單系統,以合乎飲料供應要求。

1.最初是這樣設計的:

 1 /**
 2  * 飲料抽象類
 3  *
 4  */
 5 public abstract class Beverage {
 6     
 7     protected String description;
 8     
 9     public String getDescription() {
10         return this.description;
11     }
12     
13     /**
14      * 子類需自定義自己的價格
15      * @return
16      */
17     public abstract double cost();
18     
19 }

每一種飲料都需要繼承該抽象類,並覆寫cost()方法。

2.但是購買咖啡時需要考慮到調料的部分,每種咖啡會加不同種的調料,比如蒸奶、豆漿、摩卡或者覆蓋奶泡,那麼訂單系統需要考慮加入不同調料後的價格。因此需要實現不同的子類來定義添加不同調料後的價格。大家知道,一種咖啡跟多種調料有多種組合方式,那麼多種咖啡和多種調料的組合後,幾乎是類爆炸!

 

維護極其困難:

如果某種飲料價格調整;或是新增了某種飲料,怎麼辦?

 

後來經過改進後,把是否存在某種調料作為飲料的屬性,併在飲料抽象類中實現cost方法,子類可以覆寫cost方法並設置添加的調料最終確定添加不同調料後的價格:

 1 /**
 2  * 飲料抽象類
 3  *
 4  */
 5 public abstract class Beverage {
 6     
 7     private boolean milk;//牛奶
 8     private boolean soy;//豆漿
 9     private boolean mocha;//摩卡
10     private boolean whip;//奶泡
11     
12     private double milkCost = 0.19;
13     private double soyCost = 0.26;
14     private double mochaCost = 0.29;
15     private double whipCost = 0.17;
16     
17     protected String description;
18     
19     //setter getter method
20 
21     public String getDescription() {
22         return this.description;
23     }
24     
25     public double cost() {
26         double condimentCost = 0.0;
27         if (hasMilk()) {
28             condimentCost += milkCost;
29         }
30         if (hasSoy()) {
31             condimentCost += soyCost;
32         }
33         if (hasMocha()) {
34             condimentCost += mochaCost;
35         }
36         if (hasWhip()) {
37             condimentCost += whipCost;
38         }
39         return condimentCost;
40     }
41     
42 }
43 
44 /**
45  * 低糖咖啡
46  *
47  */
48 public class Decaf extends Beverage {
49     
50     @Override
51     public String getDescription() {
52         return "It is Decaf.";
53     }
54 
55     @Override
56     public double cost() {
57         super.setMilk(true);//添加牛奶調料
58         return 1.99 + super.cost();
59     }
60     
61 }

這樣一來,如果有五種咖啡,那麼只需要實現五個子類即可,不同的子類可以靈活設置添加不同的調料。

但是這樣的設計存在一定的問題:

1)調料價格的改變會使我們改變現有代碼;

2)出現新調料,就需要加上新的方法,並改變父類中的cost方法;

3)若出現新的飲料,如紅茶,那麼新的飲料繼承該父類,父類中的調料屬性並不合適,如奶泡等;

... ...

 

設計原則:類應該對擴展開放,對修改關閉。

 

裝飾者模式思想:以飲料為主體,然後在運行時以調料來“裝飾”飲料。

例如客戶需要摩卡和奶泡深焙咖啡,那麼要做的是:

拿一個深焙咖啡對象;

以摩卡對象裝飾;

以奶泡對象裝飾;

摩卡和奶泡屬於調料,但是也是裝飾者,它的類型反映了它裝飾的對象,所謂反映,指的是兩者類型一致。那麼所有調料需要繼承Beverage。

 

使用裝飾者模式設計的代碼:

 1 /**
 2  * 飲料抽象類
 3  *
 4  */
 5 public abstract class Beverage {
 6     
 7     protected String description;
 8     
 9     public String getDescription() {
10         return this.description;
11     }
12     
13     /**
14      * 獲取每種飲料的價格
15      * @return
16      */
17     public abstract double cost();
18 }
19 
20 /**
21  * 調料抽象類
22  *
23  */
24 public abstract class Condiment extends Beverage {
25     
26     public abstract String getDescription();
27     
28 }

這裡調料繼承飲料,僅僅是為了使兩者具有相同的類型,並非為了復用父類的行為。

下麵是飲料的子類:

 1 /**
 2  * 深焙咖啡
 3  *
 4  */
 5 public class DarkRoast extends Beverage {
 6     
 7     public DarkRoast() {
 8         description = "DarkRoast";
 9     }
10     @Override
11     public double cost() {
12         return 0.19;
13     }
14 
15 }
16 
17 /**
18  * 濃縮咖啡
19  *
20  */
21 public class Espresso extends Beverage {
22     
23     public Espresso() {
24         description = "Espresso";
25     }
26     
27     @Override
28     public double cost() {
29         return 1.99;
30     }
31 
32 }
33 
34 /**
35  * 黑咖啡
36  *
37  */
38 public class HoseBlend extends Beverage {
39     
40     public HoseBlend() {
41         description = "Hose Blend Coffee";
42     }
43     
44     @Override
45     public double cost() {
46         return 0.99;
47     }
48 
49 }

調料(裝飾者)子類:

 1 /**
 2  * 摩卡
 3  *
 4  */
 5 public class Mocha extends Condiment {
 6 
 7     private Beverage beverage;
 8     
 9     public Mocha(Beverage beverage) {
10         this.beverage = beverage;
11     }
12 
13     @Override
14     public String getDescription() {
15         return beverage.getDescription() + ", Mocha";
16     }
17 
18     @Override
19     public double cost() {
20         return 0.20 + beverage.cost();
21     }
22 
23 }
24 
25 /**
26  * 豆漿
27  *
28  */
29 public class Soy extends Condiment {
30     
31     private Beverage beverage;
32     
33     public Soy(Beverage beverage) {
34         this.beverage = beverage;
35     }
36 
37     @Override
38     public String getDescription() {
39         return beverage.getDescription() + ", Soy";
40     }
41 
42     @Override
43     public double cost() {
44         return 0.23 + beverage.cost();
45     }
46 
47 }
48 
49 /**
50  * 奶泡
51  *
52  */
53 public class Whip extends Condiment {
54     
55     private Beverage beverage;
56     
57     public Whip(Beverage beverage) {
58         this.beverage = beverage;
59     }
60     
61     @Override
62     public String getDescription() {
63         return beverage.getDescription() + ", Whip";
64     }
65 
66     @Override
67     public double cost() {
68         return 0.69 + beverage.cost();
69     }
70 
71 }

測試代碼:

 1 public class ComponentTest {
 2     @Test
 3     public void test() {
 4         Beverage beverage = new Espresso();
 5         System.out.println(beverage.getDescription() + ", $" + beverage.cost());
 6         Beverage beverage2 = new HoseBlend();
 7         beverage2 = new Mocha(beverage2);
 8         beverage2 = new Mocha(beverage2);
 9         beverage2 = new Whip(beverage2);
10         System.out.println(beverage2.getDescription() + ", $" + beverage2.cost());
11         Beverage beverage3 = new DarkRoast();
12         beverage3 = new Soy(beverage3);
13         beverage3 = new Mocha(beverage3);
14         beverage3 = new Whip(beverage3);
15         System.out.println(beverage3.getDescription() + ", $" + beverage3.cost());
16     }
17 }

運行結果:

1 Espresso, $1.99
2 Hose Blend Coffee, Mocha, Mocha, Whip, $2.08
3 DarkRoast, Soy, Mocha, Whip, $1.31

 

java/IO中有很多用到裝飾者模式的設計,有興趣的朋友可以瞭解下。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、安裝監控中心 1、創建安裝目錄 2、解壓 上傳文件解壓文件 解壓 3、修改配置文件 4、啟動 如果一直出現點。只需要加大記憶體即可,記憶體至少大於1024,然後reboot重啟 5、測試 二、安裝管理控制台 上傳 1、 解壓tomcat、清空tomcat/webapps/ROOT 清空tomcat/ ...
  • 在一個分散式系統中,有各種消息的處理,有各種服務模式,有同步非同步,有高併發問題甚至應對高併發問題的Actor編程模型,本文嘗試對這些問題做一個簡單思考和總結。 ...
  • 最近這幾天在幫 "檸檬" 看她的APM系統要如何收集.Net運行時的各種事件, 這些事件包括線程開始, JIT執行, GC觸發等等. .Net在windows上(NetFramework, CoreCLR)通過ETW(Event Tracing for Windows), 在linux上(CoreC ...
  • 1、安裝3個zookeeper 1.1創建集群安裝的目錄 1.2配置一個完整的服務 這裡不做詳細說明,參考我之前寫的 zookeeper單節點安裝 進行配置即可,此處直接複製之前單節點到集群目錄 創建數據文件目錄 在數據文件目錄下添加myid文件 從數字1開始 保存退出,查看是否添加成功 修改zk1 ...
  • 設計模式-模板方法模式(Template Method Pattern) 2.1 定義 定義一個操作中演算法的框架,將一些步驟延遲到子類中去操作,使得子類可以不改變結構就可以改變一些特定的步驟. 模板方法模式很簡單.就只是使用了一個繼承(extends),其中abstractClass 叫做抽象模板. ...
  • 一、無所不在的連接 針對不通的使用場景,無線網路技術有很多種。 鑒於無線網路技術如此多樣,籠統地概括所有無線網路的性能優化手段是不可能的。好在大多數無線技術的原理都是相通的,衡量性能的指標和約束條件也具有普遍實用性。只要把影響無線性能的基本原理搞清楚,那其他問題自然也就迎刃而解了。 二、無線網路的性 ...
  • 1、安裝jdk 2、安裝解壓zookeeper 先創建文件夾 解壓zookeeper壓縮包 3、 創建配置文件zoo.cfg 4、運行測試 ...
  • JEEPlatform 一款企業信息化開發基礎平臺,可以用於快速構建企業後臺管理系統,集成了OA(辦公自動化)、SCM(供應鏈系統)、ERP(企業資源管理系統)、CMS(內容管理系統)、CRM(客戶關係管理系統)等企業系統的通用業務功能。Github鏈接:https://github.com/u01 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...