# Design Pattern [GoF Design Pattern](https://paginas.fe.up.pt/~aaguiar/as/gof/hires/patcafso.htm) ## Delegation [2019.01.03] http://best-practice-software-engineering.ifs.tuwien.ac.at/patterns/delegation.html ## Proxy [2019.01.06] > Proxy to be a specialization of the more general delegation pattern. In the Proxy both the RealSubject and the Proxy conform to the same interface, this isn't the case with delegation. virtual proxy: 可做些最架化措施,像是需要時才建立耗資源的物件 ## Facade [2019.01.09] ## Decorator [2019.01.12] #### **Applicability** - 想動態而透明地添加額外權責到個別物件身上,不影響其他物件時 - 希望權責可動態撤銷時 - 想擴充功能,但子類別繼承方式卻不切實際時 #### **Pros** - 比靜態的(多重)繼承機制更有彈性: 隨時把Decorator掛上去或拿掉,就能在執行期動態增刪權責 - 不必事先為用不到的功能付出代價,可以之後視實際需要以Decorator一點一點添加功能,且可疊加使用 #### **Cons** - Decorator不等於Component,不能仰賴物件等同性質 - 小型物件太多了 #### **Implementation** Decorator vs. Strategy [4] 1. Decorator是包在物件外面的一層皮;Strategy則是改變物件的內臟 2. 雖然Strategy方式可能得修改Component介面以留擴充餘地,不過Strategy介面的自由度很大,但Decorator的介面就受制於Component 3. Decorator是<font color=#0079c1>**由外往內**</font>改變Component,所以Component不必知道它被外覆(**Decorator對Component而言是透明的**);至於Strategy,由於Component知道有哪些擴充項目,所以必須親自記錄及維護這些Strategy物件 ## Strategy [2019.01.14] #### **Applicability** - 如果許多相關類別僅在「行為」方面有別,可用Strategy併成一個類別,還可以自由切換行為 - 當你需要同一演算法的各種變形版本時(基於各種時間/空間考量),可用Strategy將它們<font color=#0079c1>**組織成類別階級**</font> - 若演算法用了不該被外界知悉的複雜資料結構時,可用Strategy避免曝光 - 如果一個類別裡定義了好幾種行為,且這些行為都是以條件判斷指令來切換時,最好把每一條分支搬到個別的Strategy類別身上 #### **Pros** - 可定義一整族相關的演算法及行為讓Context選用,繼承機制可將各種演算法的共同功能抽取出來 - 不用把演算法時做細節混雜在Context裡(讓子類別繼承Context也能提供多種版本的演算法及行為)。改將演算法封裝在另一個Strategy類別,就可在不影響Context的情況下單獨改變引用的演算法,較易切換、理解、擴充 - 去除條件判斷式 - Strategy針對同一行為提供多種不同的實作,讓clients根據時間空間考量則一採用 #### **Cons** - clients必須對各種不同的Strategy有所認知 - ConcreteStrategy共用同一個Strategy介面,有些東西對ConcreteStrategy來說可能稍嫌多餘(例如Context傳送的初始化參數) - 物件數目增加 #### **Implementation** Strategy獲得Context資訊的方式 [1] 1. Context主動把資料餵給Strategy(作為參數傳遞),此時Strategy與Context耦合性較低,可是Context也可能送出Strategy所不需要的資料 2. 把Context自己當參數傳遞給Strategy,讓Strategy直接向Context索取想要的資料;Strategy也可以儲存一個指向Context的reference,但如此一來Context的介面就過於詳盡,Strategy和Context耦合度較高 ## Flyweight [2019.01.17] #### **Applicability** 在以下條件<font color=#0079c1>**全都成立**</font>時才考慮用Flyweight - 當應用程式使用了一大堆物件時 - 過多物件耗用過多空間時 - 物件的大部分狀態都可歸為外在狀態時 - 如果將外在狀態拿掉,就能將好幾群物件換成少數幾個共用物件時 - 如果應用程式並不仰賴物件等同性質時 #### **Pros** - 共用的Flyweight物件越多就越省空間,可共用的狀態越多也越省空間;如果物件用了越多內在及外在狀態,且外在狀態可動態計算求得而不必存下來的話,就可省下最多的空間 因此可分為兩種途徑來解省空間: - 以共享物件降低內在狀態的空間代價 - 以計算時間換取外在狀態的空間代價 #### **Implementation** - 移除外在狀態: 此模式能否奏效,很大因素在於能否挑出外在狀態,從共用物件身上移出去 - 管理共用物件: 因為物件需要共用,所以外界不應該直接具現他們,應該用FlyweightFactory取得特定的Flyweight物件來用 ## State [2019.01.18] #### **Applicability** - 當物件的行為取決於它的狀態,連執行期行為也得隨狀態而改變時 - 如果某些操作裡面,依據物件狀態而做的條件判斷式太過於龐大時。狀態通常會以列舉常數表示,許多操作裡面也常會有相同的條件判斷結構。State將每一個條件分支抽取成獨立的類別,以便<font color=#0079c1>**將物件狀態也視為另一個獨立的物件**</font>,可獨立改變 #### **Pros** - 集中處理與狀態相依的行為並予以切割: 每一個State子類別各封裝一種狀態轉移及動作,將程式執行狀態提升至整個物件狀態 - 突顯狀態轉移邏輯: 以Context的角度來看,狀態轉移應該是個不可再細分的atomic動作,觀念上只是重設一個State物件的變數而已 - 狀態物件可以共用: 如果State物件不含個體變數,Context就可共用State物件,此時State物件就相當於沒有內在狀態、只有行為的Flyweight物件 #### **Implementation** 1. 誰來定義狀態轉移邏輯? [1] - 如果轉移條件是固定的,就可全部寫在Context裡 - 更彈性的作法,是讓State子類別自己只訂下一個State是誰、何時才做轉移;這得替Context增添一個介面,讓State物件能藉以設定Cotext的現行狀態 - pros: 只要定義新的State子類別,就可修改或擴充狀態轉移邏輯 - cons: State子類別需要知道至少一個其他的State子類別 2. 實作取捨 [3] - 有需要時才產生State物件,用完就殺掉 - 事先就建立好,事後絕不殺掉 ## Singleton [2019.01.20] #### **Applicability** - 當類別只能有一個物件個體,而且還要給外界一個方便的單一窗口時 - 這唯一的物件個體必須也能透過繼承方式加以擴充,client不必修改就能使用新的擴充介面 #### **Pros** - 掌控此為一個體的所有存取動作 - 減少命名空間 - 操作和內部結構仍有擴充空間 - 允許不定個體的物件個體 - 比類別級操作更富彈性 #### **Implementation** - 確保只會有一個物件個體存在: constructor記得要private ## Abstract Factory [2019.01.22] #### **Applicability** - 當系統必須要和最終成品的生成、組合、表達方式保持獨立時 - 當系統組態必須能調整到與各陣營產品順利搭配時 - 一整族相關的物件必須一起使用,又得確保不會搭配錯誤 - 把類別程式庫貢獻出來,卻只想公開介面、不想公開實作細節 #### **Pros** - 將具象類別隔離開來: 因為factory將創建成品物件的程序與責任封裝起來,將client隔絕在真正的實作類別之外,因此client只能透過抽象介面來操控這些物件 - 易於將整族成品物件抽換掉 - <font color=#0079c1>**增進成品物件的一致性**</font>: 若整族成品物件的設計初衷,本來就是該聚在一起使用,就有必要確保應用程式在同一時間只會使用某一物件陣營。Abstract Factory目的即在於此 #### **Cons** - 難以提通新的成品物件種類: Abstract Factory的介面將「能產生哪幾種Product」寫死了;想擴充此介面,就得修該AbstractFactory類別及所有子類別 #### **Implementation** - 常與Factory Method搭配使用 [2、Sample Code]: 最常見的做法就是為每一種Product個定義一個factory method,然後各ConcreteFactory便可覆寫各自的factory method以產生特定的成品物件族系 - 如果有好幾族成品物件同時並存,不妨採用Prototype模式來製作ConcreteFactory - AbstractFactory只是<font color=#0079c1>**一堆Factory Method的組合**</font> [Sample Code] - AbstractFactory(在Sample Code中)不是抽象類別,如此才能同時身兼AbstractFactory及ConcreteFactory二職 [Sample Code] ## Factory Method [2019.01.25] #### **Applicability** - 當類別無法明指欲生成的物件類別時 - 當類別希望讓子類別去指定欲生成的物件類型時 - 當類別將權力下放給一個或多個輔助用途的子類別,又希望將「交付給那些子類別」的知識集中在一起時 #### **Pros** - 有了Factory Method,就不必再將與應用場合高度相依的類別寫死在主程式裡。主程式只須面對Product介面,因此可和任何未知的ConcreteProduct類別合作無間 - Factory Method還有兩個作用: 1. 替子類別預留掛勾: 子類別可以藉以擴增功能 2. 連接平行地位的類別階層(類似Abstract Factory?): 平行地位類別階層通常會發生在「類別將某些權責委託給其他類別處理」的情況 #### **Cons** - client必須自Creator類別衍生出子類別,才能造出特定的ConcreteProduct物件。如果不是本來就該衍生出子類別,就平白多一道手續 #### **Implementation** - Factory Method有兩種主要形式 1. Creator是抽象類別,Factory Method只是空殼子 2. Creator是具象類別,Factory Method有預設行為(note: Java抽象類別的Method也可以用預設的行為) - 參數化Factory Method: 讓同一個Factory Method能建立出多種成品物件。它有個指明生成物件「類型」的參數,所有能生成的物件都共享同一個Product介面(**不想處理的物件生成任務可以交還給父類別去完成**,參考[2]) - Factory Method常與lazy initialization併用[3] ## Prototype [2019.01.28] Objective-c將類別視為一級物件(first-class object): 任何物件都能當成creator來用,這類語言的class object本來就具有Prototype的性質 [Consequences 4] first-calss object: 在執行期,class本身可以被當成參數傳入function或被function return https://www.quora.com/What-are-first-class-objects #### **Applicability** - 當系統必須與成品物件的生成、組裝、表達方式保持獨立時[Abstract Factory Applicability 1],**以及**下面**三種情況之一** : - 在執行期才指明欲具現哪一種類別時(如: 動態載入);or - 避免造出與成品物件平行的一整族factory類別階層時;or - 當類別的物件個體只可能處於少數幾種可能的狀態時,替各種可能的狀態安置Prototype讓人複製,會比手動具現類別來得方便 #### **Pros** - 在執行期增刪成品物件類型: 只要向client註冊Prototype,就能引進新的成品物件類型。client可以在執行期才安裝或卸除Prototype,較具彈性 - 改變內容已訂出新的物件: 可透過object composition方式制定新的行為,不須再定義新類別(先以既存類別具現出物件個體,再將它餵給client做prototype;client則有prototype可委託權責,以制定新行為) [2] - 可減少系統所需的類別數量 [2] - 改變結構以訂出新的物件: Composite物件的clone()採用deep copy [3] - 減少子類別數量 [4] #### **Cons** - 每一個Prototype的子類別都必須實作clone()操作,未必是件容易的事 #### **Implementation** - 使用Prototype manager - Implementing the Clone operation: Prototype最困難的地方在於如何正確製作clone() - 萬一物件結構含有circular reference時不容易製作clone() - shallow copy versus deep copy: 以Prototype而言,萬一prototype的結構很複雜,就必須用deep copy才行,原物件和複製品才能各自獨立不相干擾 - Initializing clones(複製品): clone()通常不允許傳遞初始參數進去(為保持均一介面)。如果Prototype早已有設定狀態的操作,直接用它就行了,否則就得另外寫個initialize()來初始化複製品(Sample Code) ## Template Method [2019.02.01] Template Method vs. Strategy: Template Method 利用繼承方式來改變演算法的某些部份 Strategy 則利用委託機制來改變整個演算法 #### **Applicability** - 希望演算法的不變之處只寫一次,可變之處則留給子類別去實作時 - 如果許多子類別都會有些共同的行為,就應該抽取出來集中至另一個共同的類別,避免一再重複 - 希望能控制子類別的擴充範圍時。可以讓template method在某些定點呼叫hook operation,將可能的擴充範圍侷限在這些定點上 #### **Consequences** - Template Method是一種基本的程式碼再利用技術 - Inversion of control(IOC)、「好萊塢原理」 - hook operations, which provide default behavior that subclasses can extend if necessary. A hook operation often does nothing by default. - Template Method有必要明定<font color=#0079c1>**哪些操作是hook性質**</font>(可被覆寫)、<font color=#0079c1>**哪些是抽象操作**</font>(必須覆寫) #### **Implementation** - access control: - TemplateMethod呼叫的PrimitiveOperation要是protected,確保只會被TemplateMethod呼叫到 - 必須被子類別覆寫的PrimitiveOperation,都該宣告成abstract method - TemplateMethod本身不該被覆寫(Java可以宣告final) - 減少PrimitiveOperation的數目: 盡可能減少子類別所需覆寫的PrimitiveOperation數目 ## Composite [2019.02.14] #### **Applicability** - 想表達出「部分-全體」的物件關係時 - 想讓客戶碼不須考慮基本物件與複合物件之間的差異,能以一致的方式處理複合結構裡的物件時 #### **Pros** - 簡化client: client能以一致性的角度處理複合結構和個別物件,它通常不會知道所面對的是Leaf還是Composite - 更容易添加新的Component類型,不需修改Client #### **Cons** - 太容易增加新的Component類型意謂著難以對Composite所能包含的Component類型設限 #### **Implementation** - 父節點的reference: 設置一個由子節點指向父節點的reference,可簡化複合結構的巡訪及管理,通常會把這個reference擺在Component類別裡。另外要確保每一個子節點的reference都確實回指到它的上一層複合體 [1] - Composite的其中一個目的是不讓client確知所面對的到底是Leaf還是Composite,Component類別應盡可能涵蓋Composite和Leaf可能會有的操作。Component類別通常會提供預設的行為,Leaf和Composite的子類別也會去覆寫它們 [3] - 宣告管理子節點的操作會面臨安全性與通透性的取捨,此模式比較強調通透性[4] ## Chain of Responsibility [2019.05.]