Go學設計模式--裝飾器和職責鏈,哪個模式實現中間件更科學?
大家好,我是每周在這里陪你進步的網管~,本次我們繼續填坑,說一下裝飾器模式。
上篇文章我們說過裝飾器是代理模式的特殊應用,而且很多人說中間件是用裝飾器模式實現的,有的人說是用職責鏈實現的,那么這篇文章我們就來一起看看他們的異同。
什么是裝飾器
裝飾器模式(Decorator Pattern)也叫作包裝器模式(Wrapper Pattern),指在不改變原有對象的基礎上,動態地給一個對象添加一些額外的職責。就增加功能來說,裝飾器模式相比生成子類更為靈活,屬于結構型設計模式。
給對象添加新行為最簡單直觀的辦法就是擴展本體對象,通過繼承的方式達到目的。但是使用繼承不可避免地有如下兩個弊端:
繼承是靜態的,在編譯期間就已經確定,無法在運行時改變對象的行為。
子類只能有一個父類,當需要添加的新功能太多時,容易導致類的數量劇增。
而使用裝飾器模式,我們通過將現有對象放置在實現了相同一套接口的包裝器對象中來動態地向現有對象添加新行為。在包裝器中進行我們代碼的擴展,有助于重用功能并且不會修改現有對象的代碼,符合“開閉原則”。
這里被放置在包裝對象的“現有對象”通常會被叫做“組件”(Component),而包裝組件的包裝器對象就是我們常說的“裝飾器”(Decorator),因為裝飾器會組件實現相同接口,故客戶端無法識別兩者的差異,也就不需要在增加裝飾器時對客戶端調用代碼進行修改了。
從上面關于裝飾器模式的描述中 ,會感覺他跟代理模式很像。這是因為他們本來在結構上也幾乎一樣,裝飾器算是代理的一個特殊應用--裝飾器模式的一個特點是可以嵌套多層裝飾器,相當于給代理再加代理。不過代理強調的是對本體對象的訪問控制,而裝飾器是用來對本地進行增強,兩者在使用目的上不一樣。
上面裝飾器模式的用處特點用文字描述了這么多,下面我們用 UML 類圖展示一下它的結構,讓我們在寫代碼前對模式中的各個角色有個更清晰的認識。
裝飾器的結構
用 UML 類圖表示裝飾器模式的結構如下:
從圖中可以看到裝飾器模式中主要有如下幾個角色:
- 客戶端:會用多層裝飾器來封裝組件, 最后調用裝飾好的包裝器的方法,啟動執行。
- 組件接口:Component聲明裝飾器對象和被裝飾的組件對象要實現的公用接口。
- 組件實現:具體的組件實現類它的Operation方法中定義了組件的基礎行為, 裝飾類可以增強這些行為。
- 基礎裝飾類:擁有一個指向被封裝對象的成員變量。 在自己的Operation?方法中調用被裝飾對象的Operation方法
- 具體裝飾類:重寫父類的Operation?方法實現增強邏輯。類圖里已經給出了要實現的主要邏輯,第四步的基礎裝飾類并不需要一定存在,完全可以由具體裝飾類來持有對被裝飾對象的引用,并實現增強邏輯,這樣一來整體的結構會更簡單一些。
注意:圖中的方法名在代碼實現里可自己定義,不需要完全跟圖里給出的方法名一樣。
我們可以跟上節代理模式的UML類圖做個對比,兩者在結構上非常相似,尤其是省略了BaseDecorator這一層后,在結構上基本上是一摸一樣,這樣我們一直再強調的--"裝飾器是代理模式的特殊應用"的一個論據。
下面我們看一下實現裝飾器模式的代碼模版,本文中提供了Go語言實現一個簡單裝飾器模式的代碼模版。
裝飾器模式代碼實現
清楚了裝飾器模式結構的組成后,再來寫代碼就會清晰很多,接下來我們演示一下用裝飾器模式實現增強游戲主機的一個例子。
首先我們定義一個游戲主機的產品接口,它就是上面類圖中組件和裝飾器的公共接口。
然后我們提供一個基礎的產品實現類作為裝飾器模式中的組件。
這里給出的是一個 CD 版的游戲主機,平時玩游戲的同學都會知道,一般還會有數字版的主機,價格會便宜點,這種情況我們可以提供一個數字版游戲主機的實現作為組件實現類。
那么除了這兩種基礎的產品類型,廠商一般還會開發各種主題限定配色的主機、增加了硬件配置的主機等等,這兩種在價格上肯定會跟基礎版有些不一樣,針對這種層面的擴展我們可以使用裝飾器來實現,避免對基礎組件類的更改。
下面是用兩個裝飾器實現的Plus版和主題配色版的兩個增強。
根據裝飾器模式的特點,兩個增強還可以疊加在一起,組合出即高配主題限定版主機...... 呃,是不是有點某游戲大廠每年發新機時給你的感覺了,就是不出第二代,每年給你多發幾個限定配色、升級下屏幕,說的就是你 XXX(各位自己評論里腦補一下)
好了,在客戶端我們把裝飾器和組件組合起來就能獲得一款高配主題限定版主機......
本文的完整源碼,已經同步收錄到我整理的電子教程里啦,可向我的公眾號「網管叨bi叨」發送關鍵字【設計模式】領取。
裝飾器和幾個模式的區別
裝飾器和代理在結構上類似,在行為上跟職責鏈模式類似,現在我們總結一下他們之間的區別
裝飾器模式 VS 代理模式
- 裝飾器模式就是代理模式的一個特殊應用。
- 裝飾器模式強調自身功能的擴展。
- 代理模式強調對代理過程的控制。
裝飾器 VS 職責鏈模式
裝飾器和職責鏈在行為上看都是多個單元進行組合完成邏輯處理,但是裝飾器注重給某樣東西添加擴展,最終會得到一個產品。而職責鏈更強調分步驟完成某個流程,更像是一個任務鏈表,而且與裝飾器模式不同的是,職責鏈可以隨時終止。
舉個例子來說,針對OA系統請假審批這個場景,假設員工請假需要得到組長、總監和經理的批準才行。在這種情況下,使用裝飾器模式實現的話無論您的請假在前面的環節被批準還是被拒絕,整個鏈條都不會中斷,最終我們會得到三個級別審批人對申請的全部反饋。
而使用職責鏈模式的話,在每個階段,每個審批人都有權批準或拒絕。如果請求在任何級別被拒絕,那么整個流程就會結束,請求不會繼續流轉到下一個級別的審批人那里。
所以看到這里,你覺得像Web框架的中間件這種東西應該拿職責鏈還是裝飾器實現呢?
總結
裝飾器模式有不少優點,它是繼承的有力補充,比繼承靈活,在不改變原有對象的情況下,動態地給一個對象擴展功能,即插即用。通過使用不同裝飾類及這些裝飾類的排列組合,可以實現不同效果,完全遵循程序設計的“開閉原則”。
但裝飾器的使用必將會給程序帶來更高的復雜性,更低的可讀性,子類集成的代碼結構會更直白易懂一些,而且雖然裝飾器符合“開閉原則”,但是它會給程序帶來更多的類,動態裝飾在多層裝飾時會更復雜。
所以總體上使用裝飾器模式的時候也是兩害相較取其輕,為了不頻繁修改已經成型的子類而引入更多裝飾器類。
應用的時候一定要謹記裝飾器是“增強”某個事物用的,可千萬別把事物本身實現的主邏輯用裝飾器實現了。