Re: [模式] 裝飾者模式(decorator)只有一種結構嗎?

看板OOAD作者 (....)時間12年前 (2013/01/14 17:15), 編輯推噓2(207)
留言9則, 3人參與, 最新討論串4/5 (看更多)
或許回歸到 Decorator Pattern 的 Intent 來看會較清楚 Intent of Decorator (from GoF Book): Attach additional responsibility to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality. 由上得知, 使用 Decortor 所欲表達物件關係/行為是: * 系統中有一種特殊的 Object, 姑且稱之為 Component * Component 常常有額外附加 responsibility 的需要 * 額外附加 responsibility 的動作需要能動態給定 (因此直接 subclassing 並不適宜) 有了以上理解後, 再回頭看看 Decorator 結構上的兩個重點 1. Decorator decorates (as an aggregation) a Component Decorator 包裝了 component, 透過這層包裝 Decorator 可在正式將責任交付給 component 之前(或之後) 額外進行所欲附加的行為 2. Decorator is a Component 為了確保 Client 仍可正常使用 "包裝" 後的 Component Decorator 必須也符合 Component 所制定的 Interface 這可透過 Inheritance 達成 (Decorator extends Component) ----------------------------------------------------------------------- 最後, 原 Po 的例子中 使用 Component aggregate a list of Decorator 的形式來表達此一關係 並使用 Iteation 的方式來執行附加的 responsibility 在此例中似乎是 OK 的, 但回歸到 Decorator 的 Intent, 有兩點關鍵不同 1. 附加 Responsibility 的彈性 在原 Po 例子中, 主餐 (Component) 決定了"如何附加 Responsibility" (使用 Iteration) 這限制了附加 Responsibility 的彈性 使用 Decorator Pattern 時 "如何附加 Responsibility" 完全由 Decorator 透過 override 決定 2. Component 的實做不會因為 Decorator 的採用而有所改變 在原 Po 例子中, 需要修改 "主餐 Class" 才能完成欲達成目標 但使用 Decorator Pattern 完全不用 修改 "主餐 Class" (Component) 即可達到為 Component 動態附加 Responsibility 的果效. -- 我個人的感覺是 此例似乎無法很自然的與 Decorator Pattern 的 Intent 呼應 所以可能會有許多 Alternative Design 的想像空間 我建議參考 GoF Book 中例子, 或許會有更深的體會 ※ 引述《worldxxi ()》之銘言: : 謝謝q大的回覆,但是我還是不太懂,先說一下,我並沒有一定要說我的想法 : 比較好,只是想了解一下差異,在您比較兩者差異的時候我的想法被當作 : 不能有輸入,似乎不太公平,如果要比較應該有同樣的立足點,同時,我修改class名稱 : 讓它看起來比較舒服,我直接修改在下面(引文中修改)。 : P.S. : 會一直有疑問是因為我總覺得,兩者的結構是完全等價的,但是可以用多型解決的問題 : 以那麼多前人的經驗不可能硬要弄出新的pattern,所以somehow我的想法一定有缺陷無法 : 應付變動,而我就是想要知道那個問題是什麼? : ※ 引述《qrtt1 (有些事,有時候。。。)》之銘言: : price=130; : foreach(decorator in list){ : price = decorator.cost(price); : } : return price; : abstract class Decorator : class 味增湯 : Decorator : //handle price : return newPrice; : //實際使用長成這樣 : main(){ : unDecorator = new 某主餐(); : unDecorator.addDecorator(new 味增湯); : unDecorator.addDecorator(new 優惠時段); : : 我想這世上沒有一定得用什麼樣的解法的規則。 : : 學習這些『前人』設計上的經驗, : : 只是輔助我們在遇到問題時多一個選項可以考慮。 : : 依你的想法修改後,問題的複雜點會集中到每一個 : : ConcreteComponent 的 behavior, : : 也就是 豬排.cost(); : : 現在你想得只是單純的『加法』將 list 內的副餐『加』起來那麼單純。 : : 如果出現了例外的情況,例如:麥當當晚間,二人同行第二套半價。 : : 雖然這個 bussiness logic 不是一種『餐』, : : 但毫無疑問的,它的責任落在 cost mehtod! : //ConcreteComponent不動(我的表達不好,這其實是我原本的意思) : public int cost() { : price = 130; : foreach(decorator in list){ : price = decorator.cost(price); : } : return price; : } : //假設有兩個Decorator裝飾,先用味增湯裝飾,再用優惠裝飾 : //味增湯的cost : public int cost(price){ : //傳進來的price現在是130 : return price + 20; : } : //優惠的cost : public int cost(price){ : //傳進來的price現在是150 : return price * 0.5; : } : 所以並不會有因為不同情況或需求,導致所有事情的責任都落在 : ConcreteComponent的cost上面。 : 我會說等價的原因是因為在直觀上: : 1. 不停用new包裝的過程等於就是讓我想法中的decorator list不斷增加 : 2. Decorator Pattern在呼叫cost時,是利用繼承架構得知目前要呼叫的 : 實際上是哪個cost,而我的想法只是明確的指出是誰做。 : : 當你有多種 ConcreteComponent 要套新的規則時, : : 你都得針對每一個 ConcreteCompoent 去修改它。 : : 用 Decorator 你就用裝飾的角度來看它: : : 餐點 = new 優惠時段的(new 含泡菜的(new 含味噌湯的(new 排餐主餐()))) : : 結帳金額 = 餐點.cost() : : 對整體來說,只要各別維護不同餐點的單價,與優惠策略的選擇: : : *. 早餐優惠 : : *. 晚餐優惠 : : *. 壽星優惠 : : *. VIP 優惠 : : *. 消費總金額優惠 : : *. 組合套餐優惠 : : 動態地多 wrapper 一層上去,維護範圍相較起來單純, : : 會改到的是各別的裝飾者、實體、最終結帳的 caller。 : : 整體來說,這比較符合 OCP 原則。 : : 學習 design pattern 單純看結構可能沒 fu, : : 但代入了『改變』後, : : 評估怎麼做對『既有』程式影響最小 : : 才是我們希望得到的。 : : 其它參考文章: : : http://www.ptt.cc/bbs/java/M.1243696485.A.DD1.html -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 140.116.72.58 ※ 編輯: FTM 來自: 140.116.72.58 (01/14 17:17)

01/14 17:33, , 1F
感謝mars90226, qrtt1, FTM,我想我可能真的需要看
01/14 17:33, 1F

01/14 17:37, , 2F
GoF的書才能理解 F大的解釋讓我好像有點感覺但又很
01/14 17:37, 2F

01/14 17:42, , 3F
很模糊 我試試看研究StreamDecorator的例子
01/14 17:42, 3F

01/14 17:45, , 4F
我好像找到我卡住的點了 就是我的例子中Decorator只
01/14 17:45, 4F

01/14 17:47, , 5F
用到price,如果功能上差異很大就必須動到Component
01/14 17:47, 5F

01/14 17:50, , 6F
增加Decorator才會用到的member
01/14 17:50, 6F

01/14 18:06, , 7F
很好的切入點,希望 worldxxi 早日打通盲點 :)
01/14 18:06, 7F

01/14 18:07, , 8F
上班後累了,如果有閒。假日再繼續討論orz
01/14 18:07, 8F

05/14 12:57, , 9F
推~
05/14 12:57, 9F
文章代碼(AID): #1GyyotRA (OOAD)
文章代碼(AID): #1GyyotRA (OOAD)