橋接模式 VS 裝飾器模式、狀態模式 VS 策略模式的微妙之處
Foundations of Software Engineering 的 Design Pattern 學習筆記整理
關鍵詞:設計模式、橋接模式、裝飾器模式、狀態模式、策略模式( Design Pattern, Bridge Pattern, Decorator Pattern, State Pattern, Strategy Pattern)
本文簡述了橋接模式(Bridge Pattern)和裝飾器模式(Decorator Pattern),以及狀態模式(State Pattern)和策略模式(Strategy Pattern)的微妙之處,以及它們之間是如何“看起來就像另一者的”。
橋接是指能夠沿著不同的維度(along distinct dimensions)以多種方式(in more than one way)來指定一個對象,通常使用子類型(sub-typing)和注入(injection)
new BoldText("blah blah", new UTF16Encoding)) new ItalicsText("blah blah", new ASCIIEncoing)) // where BoldText and ItalicsText are subclasses of Text and UTF16Encoding and ASCIIEncoding are subclasses of TextEncoding
裝飾器是指能夠以一種任意的方式向一個對象添加特徵(responsibilities, embellishments, or features),通常使用包裝(wrapping)
裝飾器所描述的是一種 specialization,但是有著截然不同的機制,並且可以創建的變化不一定是不同維度的(variations you can create are not points on distinct dimensions):在同一維度上,多個特徵可以同時添加到一個對象上。使用裝飾器模式需要依賴繼承,但是子類型卻並不特殊化目標對象(subtyping does not specialize the target object),我們可以使用包裝來完成這個事情(injection it into a higher level object (wrapper) does the trick)。
new Underlined(new Italics(new Bold(new Text("blah blah")))).
在上面這個例子中,我們可以使用裝飾器模式將 blah blah
變成斜體的、加粗的、帶下劃線的表示,但是不能使用橋接模式將這段文本變成既是加粗又是斜體的,因為 Bold 和 Italics 都是同一個維度下的分化(specializations along the style dimension),你必須選擇其中之一。同樣,UTF16 和 ASCII 是編碼維度下的不同分化,你也必須選擇其中之一。
如果你想用橋接模式創建一個既是粗體、又是斜體的風格,那麼你就必須定義一個名為 BoldAndItalicsText
的 Text 的子類,這顯得不合理是嗎?是的,所以,你就會意識到,粗體、斜體當然是可以被一起使用的,因為它們是特徵,而不是單一維度下面的不同分化,所以你需要的其實是裝飾器模式,而不是橋接模式。
再來回顧一下這個使用橋接模式的經典例子:在一個維度上,我們有 NoSQL DB、SQL DB 和 Mock DB,它們是單一維度下的不同分化,而在另一個維度上,我們有 Backlog DB 和 Product DB 等。
狀態模式和策略模式意外地有著類似的類圖,但是它們的意圖是不同的。
狀態不僅僅表達了在運行時去改變一個對象的行為,它更加強調的是一個對象能夠識別它自己的內部狀態並相應地改變它的行為(也可能是改變它的狀態,因此需要在對象內部實現一個狀態機)。
作為策略模式的典型例子,我們會在一個機器人對象中注入一個不同的防撞策略,這時我們並沒有更改機器人的內部狀態,所以這僅僅是選了不同的策略。當我們在一個項目經理對象中注入一個不同的報告生成過濾器時,我們沒有改變內部狀態,只是改變了在任何狀態下的報告的打印策略。
但是,如果需要一臺自動售貨機在有足夠的錢存入時與錢不夠時表現不同,這就是關於狀態的了。自動售貨機在有足夠資金的時候會切換狀態,並且在每個狀態下只能執行某些行為。
因此,意圖在設計模式中是很重要的。不同的意圖意味著,不同的選擇。