Design Pattern(3) - Decorator
April 18, 2017以下文章是閱讀 深入淺出Design Pattern 還有 聖經還有Source making的筆記 圖片截圖自lynda.com的Foundations of Programming: Design Patterns 要更深入的理解一定要去看這兩本書
開咖啡店囉
今天想設計所有的咖啡的class 目前主要就賣四種咖啡
每個subclass各自overwrite cost()
但是其實咖啡可以加很多料 牛奶 豆漿 摩卡 奶泡 每個都有不同的價格 要是所有排列組合都創一個subclass…
崩潰 而且之後每加個新配料 class數目都是指數性的成長
重新設計 把所有責任丟給parent
好 今天如果要Houseblend加豆漿跟奶泡
算錢的時候一一確認
這個設計有什麼問題呢
1.某個配料價錢一改 Beverage code要改
2.有新的配料 Beverage code要改
3.不彈性 某些咖啡跟某些配料不可能配一起 可是setMocha, setSoy這些function是compile-time繼承自Beverage的 不能不要
4.如果今天想要雙倍摩卡 雙倍奶泡? 只用hasMocha是無法算錢的 還要算MochaCount等等
天啊怎麼辦啊啊啊啊啊
開放封閉守則
Classes should be open for extension but closed for modification
DP rule#5 類別應該開放以便擴充 應該封閉禁止修改
在這裡 應該把Beverage這個code給封閉 我們不希望每次有新的配料或新的咖啡 就必須改動beverage
那要怎麼開放呢 簡單 只要把咖啡跟配料的super class用一樣就可以了 咖啡跟配料都是Beverage
當然也不是說奶泡跟濃縮咖啡是同一個class 只是咖啡跟配料都繼承了Beverage
差別差在 配料多了一個指到Beverage的reference 畢竟咖啡可以單獨存在 但配料不行
為什麼這樣就解決了問題了呢
先來看一下Beverage 這是我們說好要封閉的東西
再來看一下HouseBlend 就是某一種繼承了beverage的咖啡
主角登場
沒錯 就這樣 這裡可以是interface可以是abstract class
那來看看Soy(這裡的Soy是個配料)
每個配料需要有一個指向Beverage的reference 這也是配料跟主要咖啡的區別 那main function怎麼call呢
STDOUT:
這個有意思了 Soy需要一個beverage instance 這個是他裝飾(decorate)的對象 也是Soy的constructor需要丟進來的東西
這樣事情簡單多了 每一個配料的input都是Bevarage的instance 我把它加上配料後也是變成一個beverage 這樣就可以一路chain下去 我們跳脫了配料是咖啡的attribute的思維
一個未裝飾的東西(Beverage) 丟進裝飾的物件(Condiment)的constructor 回傳一個裝飾過的東西(Beverage)
簡直精彩
Decorator Pattern
時機
想動態(runtime)增加或取消責任到個別物件 而不是加到整個類別裡
結構
-
Component(Beverage): 制定你想要動態增減責任的interface
-
ConcreteComponent(HouseBlend): 定義可以被decorate的物件
-
Decorator(CondimentDecorator): 繼承Component 但多了一個指向Component物件的reference
-
ConcreteDecorator(Soy): 繼承Decorator 實作責任本身 你想怎麼裝飾ConcreteComponent
優缺點
1.比compile time的繼承更有彈性 可以在run-time把Decorator加上去 想要任何排列組合都行
2.避免了把太多功能加上class階層的頂端
3.因為新增Decorator實在方便 很容易到後面會有一堆功能非常類似的Decorator
Decorator vs Strategy
這兩個Pattern都讓我們可以在run-time變動一個物件的特性 主要差別有二
1.Strategy是改變特性 Decorator是增加特性
2.Strategy pattern, 使用的人必須知道有什麼strategy option跟現在apply了哪一個strategy(就是你必須有一個strategy物件) 但Decorator Pattern不用 因為Decorator像是從外面改變了這個object(被包了一層)
大家常說Decorator像是換皮變臉 但Strategy像是換了骨頭
一個畫皮 一個畫心
留住你一面 畫在我心間 誰也拿不走 初見的畫面
<畫情>畫情>
Python 裝飾器
這篇文章漂亮的詮釋了Python如何利用First class function的特性來實作Decorator 淺顯易懂 由這篇文章你可以看出Decorator有多強大
First class function意思就是說function可以被傳入其他的function當做input argument 也可以被當return value傳回來
符合這個先決條件 就是使用Decorator的好時機 因為你輸入輸出是一樣的東西 你可以動態無限包裝很多個Decorator