設計模式-裝飾模式

知識海洋中的浮萍發表於2021-12-01

 

 

 

 結構圖解釋:

Component 可以是介面,也可以是抽象類,目的是指明Operation(),也就是規範要做什麼。
ConcreteComponent 一個類,繼承或者實現Component。是被裝飾的代表,例如飯,奶茶。
Decorator類 裝飾類,內部一定有Component或者ConcreteComponent的屬性,表明用於裝飾誰(擴充套件誰)。
ConcreteDecoratorA,ConcreteDecoratorB 我理解是各種裝飾物(擴充套件)
 
如果只有一個ConcreteComponent而沒有抽象的Component,那麼Decorator類可以是ConcreteComponent的子類
 
何謂裝飾模式?
例如,我現在有碗白飯,這飯上可以放土豆絲,放白菜,放豆乾,放鴨腿,這就是鴨腿飯了。
那我放土豆絲,放白菜,放雞腿,放豆乾,這就是雞腿飯了。
放不同的配菜,就是不同的套餐,如果我現在的選單上有下述幾種套餐:
雞腿飯(雞腿+青菜+香乾)
肉絲飯(肉絲+青菜+香乾)
大排飯(大排+青菜+香乾)
鴨腿飯(鴨腿+青菜+香乾)
東坡肉飯(東坡+青菜+香乾)
 
要實現這幾種飯
先用最容易想到的方式實現,每種套餐建立一個類,那麼就有雞腿飯類,肉絲飯類,大排飯類...
如果我現在雞腿飯+一個大排你怎麼辦?
難道在建立一個雞腿飯+一個大排的類?
那如果我要再加一個東坡肉呢?再加一個鴨腿呢?(這真有錢啊)
 
現在就發現我們這個實現方式就並不合適了。
我們先來分析一下,不同套餐其實就是不同的配菜加米飯對不對,像不像再給米飯的基礎上一直疊加東西。
用裝飾模式程式碼實現如下
 
/**
 * SX小吃 我這裡賣
 * 雞腿飯(雞腿+青菜+香乾)
 * 肉絲飯(肉絲+青菜+香乾)
 * 大排飯(大排+青菜+香乾)
 * 鴨腿飯(鴨腿+青菜+香乾)
 * 東坡肉飯(東坡+青菜+香乾)
 * @author wrj
 * @description
 * @Date 2021/12/1 2:37 下午
 */

abstract class Food { public abstract void has(); } //米飯 class Rice extends Food{ @Override public void has() { System.out.println("有米飯"); } } //裝飾類 abstract class FoodDecorator extends Food{ public Food food; public void addFood(Food food){ this.food = food; } @Override public void has() { food.has(); } } //雞腿 class Drumstick extends FoodDecorator{ @Override public void has() { System.out.println("有雞腿"); super.has(); } } //肉絲 class ShreddedMeat extends FoodDecorator{ @Override public void has() { System.out.println("有肉絲"); super.has(); } } //大排 class PorkRibs extends FoodDecorator{ @Override public void has() { System.out.println("有大排"); super.has(); } } //青菜 class Vegetable extends FoodDecorator{ @Override public void has() { System.out.println("有青菜"); super.has(); } } //豆乾 class DriedBeanCurd extends FoodDecorator{ @Override public void has() { System.out.println("有豆乾"); super.has(); } } public class SXFood {
   //變數寫中文為了方便理解 並不標準
public static void main(String[] args) { //標準雞腿飯的構建過程 System.out.println("雞腿飯:\r\n"); Rice 米飯 = new Rice(); DriedBeanCurd 豆乾 = new DriedBeanCurd(); Vegetable 青菜 = new Vegetable(); Drumstick 雞腿 = new Drumstick(); 雞腿.addFood(青菜); 青菜.addFood(豆乾); 豆乾.addFood(米飯); 雞腿.has(); //特殊需求 雞腿飯+大排 System.out.println("雞腿飯+大排:\r\n"); Rice 米飯1 = new Rice(); DriedBeanCurd 豆乾1 = new DriedBeanCurd(); Vegetable 青菜1 = new Vegetable(); Drumstick 雞腿1 = new Drumstick(); PorkRibs 大排 = new PorkRibs(); 雞腿1.addFood(大排); 大排.addFood(青菜1); 青菜1.addFood(豆乾1); 豆乾1.addFood(米飯1); 雞腿1.has(); } }

 

最後輸出:

 

裝飾的過程感覺像構建一個連結串列的過程
可以看到 裝飾的過程類似於構建一個連結串列的過程,每個連結串列的節點呼叫has方法時都會去呼叫super的has從而呼叫下一個節點的has直至最後一個節點
 
設計模式-裝飾模式

 

這種場景就適合用裝飾模式了。回過頭來再看裝飾模式的概念:
動態地給一個物件新增一些額外的職責。就增加功能來說,裝飾器模式相比生成子類更為靈活。
上面的雞腿飯+大排的場景就體現了動態新增的特性以及為什麼比子類更靈活因為我們不用一直生成子類,我們可以任意加東西來實現功能
 
可以看到 不同的搭配最終組合成了不同的套餐,這樣不管有什麼奇葩的需求,都不用改類,只需要改裝飾的內容和順序即可。
這也體現了開閉原則,不用改變基礎的類,只需要改變使用方法即可。
 
需要注意裝飾的順序,順序地不同,產生的結果也是不同的。這點需要注意

總結:

裝飾模式是為已有功能動態地新增更多功能的一種方式,當系統需要新功能時,是向舊的類中新增新的程式碼。這些新加的程式碼通常裝飾了原有類的核心職責或主要行為。

在主類中加入了新的欄位,新的方法和新的邏輯,從而增加了主類的複雜度,而這些新加入的東西僅僅是為了滿足一些只在某種特定情況下才會執行的特殊行為的需要(參考雞腿飯加一個大排的場景)。

裝飾模式提供了一個非常好的解決辦法,它把每個要裝飾的功能放在單獨的類中。並讓這個類包裝它所要裝飾的物件。因此,當需要執行特殊行為時,客戶程式碼就可以在執行時根據需要有選擇地、按順序地使用裝飾功能包裝物件。

優點:把類中的裝飾功能從類中搬移出去,簡化原有的類。有效地把類的核心職責和裝飾功能區分開。

相關文章