終於進入了第三章─ 裝飾器模式!首先可以稍微回顧一下第一章提到的其中一個原則─ 多用"組合",少用"繼承",這章會以物件組合的方式,避免繼承濫用的問題,並可以在執行期間對物件進行裝飾!至於這裡的裝飾指的是什麼,請繼續看下去...
本章舉的例子是一個具有各種咖啡的點餐系統,乍看之下跟第一章的各種鴨子有點像,不過是變成了各種自由發揮的咖啡!可以看到最基本的咖啡是具有描述與價格的...
但咖啡每個人可以自由加入各種調味品,例如牛奶、糖、冰量、不同的咖啡豆等,每個人愛好的份量也不同 (雙份、摩卡、去冰...),此時該怎麼處理呢?既然這些咖啡都擁有的一樣的attributes─敘述與價格,一樣最無腦的繼承一波!(然後就爆炸了...)
要如何改良呢?書本給出一種改良的寫法...
但有一些明顯的缺點,(又是大家來找碴時間XD)例如如果牛奶價格改變,我們就必須在每個飲料類別中去修改cost()
的計算方式,不符合封裝概念!又或者這種架構,也無法動態調整份量,例如要加雙倍的牛奶,照原本的寫法就必須變成多一個doubleMilk
屬性?又或者需要把布林值改成整數數值...
但感覺還有更好的方法...例如我今天要的只是最基本的"黑咖啡",這個物件類別卻需要繼承各種我永遠不會想要加的配料......
不妨想想看你會如何設計呢?設計在這個情境中會有哪些缺點?
我自己第一時間則是想到第一章鴨子的case,想把不變的部分拉出來,變化的部份封印起來,這樣是不是應該抽出一些糖度、冰量、咖啡的class?例如這樣...
至於有何問題,跟裝飾器模式比起來有何異同,可以明天再比較看看!
接下來就要介紹裝飾器模式登場了...除此之外,來看看另外一條登場的原則,以及裝飾器模式如何實踐!
延伸之前的第三個原則,我們已經知道繼承會造成行為在編譯時就固定,而組合則能帶來在執行期彈性變化的效果,除了改變原本的內容,甚至可以擴展其他原本沒有的部分,且不會動到原本封裝起來的部分。這就點出了一個重要的原則:
類別應該歡迎擴展,但拒絕修改
原本程式經過驗證的部分,我們不希望改變它,因此拒絕修改,但為了保持彈性,因應不斷的變化,我們希望可以輕易地去擴展原本的類別,這樣就可以讓我們的程式更加地穩固,卻又不僵化。
在策略模式中,最後的類別圖就是使用 "組合" 而非繼承來達成這個好處;另外前面的觀察者模式也拒絕修改subject的部分,但卻又可以自由地新增observer來進行擴展;接下來的裝飾器模式,一樣可以看到實踐了這個原則,讓我們繼續看下去...