Design Pattern

類別圖:符號


在 Head First 書中看到較多使用,結合(association)、繼承,沒有依賴

  • 單向結合
    • 起始方 會 吃(使用) 箭頭方的物件,並加以使用
    • 快記: 吃他 (指箭頭指向位置)
  • 繼承
    • 箭頭方--為父類別(介面)
    • 快記: 繼承他, 實作他 (指箭頭指向位置)


模式介紹


Strategy 策略模式

  • 目的: 切開 客戶 與 演算法(行為)
  • 定義: 封裝演算法家族,讓演算法變動時,不影響使用該演算法的程式
  • 演算法(行為),可以在 Client 物件建立後,才動態加入或隨意更換
  • 作法: 
    1. 建立 abstruct class 給客戶端繼承
      1. 建立行為接口
      2. 建立動態指定行為物件的接口、屬性
    2. 客戶端(ex. RedheadDuck)實作,直接對行為屬性操作,如: flyB.fly()
    3. 建立行為介面,並實作
    4. 客戶端隨時可透過 setAtkBehavior() 更換行為物件

Observer 觀察者模式

  • 目的: 通知 有註冊的觀察者
  • 定義: 定義了物件之間 1:N 關係,當其一個物件改變狀態,其他都能收到通知並更新。
  • java.util 有 Observer 介面 & Observable 類別可直接使用
  • 作法: 
    1. 主題物件必須實作 註冊、移除、通知觀察者
    2. 觀察者,需實作 被通知後的更新
    3. 每個主題可有 N 個觀察者
    4. 每個觀察者必須先跟主題 註冊 才能開始觀察

Decorator 裝飾模式

  • 目的: 不改變介面,但加入責任
  • 定義: 動態的將責任加在物件身上,裝飾者比繼承提供更有彈性的擴充功能
  • 雞包蛋是,紙包雞是
  • 作法:
    1. Component(drink) & Decorator 是抽象類別
    2. Mocha & Latte 是基礎類別,實作 drink
    3. 從 decorator 實踐一個 ConcreteDecoratorA 物件
      1. 保存上一個(super) drink 物件
      2. 實作 cost(), desc()...功能,直接操作 保存的 drink 物件
    4. Decorator 抽象類別可以自行加上新的方法
    5. 假設實踐了一個 裝飾者A 叫做 milk,
      使用方式 new milk(new Mocha).cost()

Simple Factory 簡單工廠 (算Coding習慣,非模式)

  • 目的: 減少 new 物件帶來的相依性
  • 定義: 封裝N個建立物件的程式碼,統一出口
  • 產生物件的類別,稱作工廠,工廠會生很多物件出來
  • 作法:
    1. 將 N 個會產生相似 物件的 create 程式碼拉成新 Class, SimplePizzaFactory,稱作工廠
    2. SimplePizzaFactory 會 依照type 來 return Pizza 物件
    3. 主程式 Store 直接將需求的 type 參數給 工廠,直接產生 Pizza 物件並操作
    4. 用法:
      1. SimplePizzaFactory.createPizza(cheese);

Factory Method 工廠方法模式

  • 定義: 讓次類別決定要實體化的類別為何
  • 由工廠方法決定產生的物件
    • 抽象類別 abstract Product factoryMethod(type);
    • 次類別 需實作方法 Product factoryMethod(type){if(xxx) return Product;}
  • 與簡單工廠、抽象工廠的差別
    • Simple Factory 1個 creator
    • Factory Method N個 creator,1個抽象產品類別
    • abstact Factory N個 creator,N個抽象產品類別
  • 作法:
    • 呈 上例
    1. 很多個 Store 產生了很多 Factory,若要統一管理,又變成 if else 判斷用哪個 Factory
    2. 改變 create 物件的方式,改由 Store 自己的工廠去實踐
    3. 每個 PizzaStore 都必須實踐工廠方法 createPizza(type)
    4. orderPizza() 再去呼叫 createPizza() 方法來操作
    5. 用法:
      1. NYStore.factoryMethod(cheese);
  • Creator 可以決定 由哪個 Product 執行 (在 factoryMethod() 中仍需用 if else 判斷)
    • 每增加一個 Product,Creator 就增加一個相依的對象
    • 守則"依賴抽象類別,不依賴具象",因此產生一個 Creator 的抽象類別
  • 示意
    • createPizza() 為 工廠方法,傳入 type 之後,會從 Pizza 物件中挑出指定 Product 回傳

Abstract Factory 抽象工廠模式

  • 定義: 提供一個介面,建立相關or相依物件之家族,而不需明確指定具象類別
  • 相較於 factory Method,會將明確的具象類別寫在 createPizza() 中,
    abstract Factory 則是由 Client 端 給予
  • 角色: Factory(工廠)、Product(產品)、Client(客戶端)
    • 工廠可以創建 N 個不同 類型 的Product
  • 工廠方法vs抽象工廠
    • 工廠方法常和抽象工廠一起使用
    • factory method針對的是一個產品等級結構   
    • abstract factory是多個產品等級結構的
    • 區別:
      • factory method 只有一個抽象產品類,而抽象工廠模式有多個。
      • factory method 的具體工廠類只能創建一個具體產品類的實例,而抽象工廠模式可以創多個
  • 作法:
    1. 建立抽象產品(ex. cheese),並實作
    2. 建立抽象工廠,實作 CreateProduct(),回傳 Product 實體(由 Factory 自己決定)
    3. Client 實作 CreatePizza()
      • 可使用 NY 或 Chicago Factory
      • cheese = factory.CreateCheese()
      • 其中所包含的 Product 將會有所不同

Singleton 獨體模式

  • 目的: 只允許存在一個實體
  • 定義: 確保一個類別只有一個實體,並給他一個存取的全域點(global point)
  • 注意,多執行續必須另外處理(兩種方式)
    1. 在屬性宣告後,優先實體化。缺點是沒用到也會建立他
    2. 雙重檢查上鎖
      1. 在屬性加上 volatile
      2. getInstance() 加上檢查 null & syncronized(Singleton.class)
  • 作法: 
    1. 利用封閉屬性儲存自己
    2. 所有使用此物件都必須從 getInstance() 取得實體
    3. ex. Calendar.getInstance()

Command 命令模式

  • 目的: 封裝請求 (把調用方法封裝起來)
  • 定義: 將 請求 封裝成物件,以便使用不同的請求,參數化其他物件。也支援命令復原。
  • 角色:
    1. Invoker 發布命令物件 (吃 command)
    2. command 命令物件 (吃 recevier)
    3. Recevier 被控制的物件(接收者)
    4. 操作:
      1. 在 client,產生 command 物件
      2. 在 client,將 command 丟到 invoker 中 (可以是N個)
      3. Run invoker (有點像是跑排程,會照順序執行)
  • 作法:
    1. 建立 ConcreteCommand,吃 Recevier 物件,並在 execute 中實作
    2. 建立 Invoker
      • 允許吃 Command 物件,並存在屬性中
      • 可以依照順序將 Command 一個個 execute
    1. 建立 Client
      1. 產生 Recevier,放入 ConcreteCommand
      2. 產生 Invoker,將 ConcreteCommand 放入 Invoker
      3. 執行 Invoker

Adapter 轉接器

  • 目的: 將一個介面 轉成 另一個介面
  • 定義: 將介面轉成另一個介面,讓原本不相容的類別可以合作
  • 讓鴨子看起來像鵝
    1. Client 只吃 鵝(介面)
    2. 建立一個 adapter 並繼承 鵝介面
    3. 讓 adapter 能吃 鴨子(介面)
    4. 用鴨子去實踐 鵝介面中的 function

Facade 表象模式

  • 目的: 簡化介面
  • 定義: 提供一個統一的介面,用來存取次系統(1orN個介面)
  • 將客戶 vs 次系統 中鬆綁 (只需要對該介面操作)
  • 將所有要操作的介面傳入 Facade 再操作

Template method 樣板方法模式

  • 目標: 定義演算法大綱,讓次類別可變更某些細節
  • 定義: 演算法定義在方法中,演算法中的某些方法可以定義在次類別中,讓次類別在不影響演算法的情況下,重新定義演算法中的某些步驟。
  • 作法:
    1. 建立一個 abstruct Class
      1. templateMethod() 放演算法
      2. primitive opration() 提供給次類別實作(必要)
      3. concreteOperation() 為自身擁有的 method,可給自己 or 次類別使用
      4. hook() 掛勾,為次類別有需要才實作(非必要)
      5. 操作
        1. 產生ConreteClass 在執行 templateMethod()

Iterator 反覆器

  • 目的: Loop 取得元素
  • 定義: 讓我們取得 聚集 中的每個元素,而不須暴露實踐方式
  • 作法:
    1. 實作一個 concreteIterator
      • 針對傳入的聚集(items清單),來實作相關method
    2. 實作一個 concrete聚集
      • 在 createIterator() 中,將產生的 concreteIterator 回傳
    3. 使用:
      1. Client 呼叫 concrete聚集.createIterator() 取得反覆器
      2. 利用反覆器取得單一個元素

Composite 合成模式

  • 目的: 樹狀結構
  • 定義: 讓你的物件能合成數狀結構,呈現"部分/整體"階層關係。但仍可用一至的方式處理個別&合成物件。
  • 角色:
    • 元件 -- 擁有葉子 & 合成物件 所有實作的method
    • 合成物件 -- 需實作 add  & remove 等方法,讓物件能夠產生階層關係
    • 葉子 -- 沒有子物件
  • 作法:
    1. 建立 Component 抽象物件
      1. 將葉子、合成物件需要的方法開好
      2. 在每個必要實踐的方法中,塞入 error Exception
    2. 建立葉子,因為沒有子物件,只需實踐基本功能
      1. createIterator() 可以建立一個 NullIterator 並回傳
    3. 建立 Composite 物件
      1. 同樣實踐 add & remove 等階層相關的功能
      2. 在屬性存放 子Component 清單
      3. createIterator() 回傳 合成反覆器
    4. 建立 CompositeIterator 物件(不需要則可忽略)
      1. 實踐 Iterator 介面
      2. 如要做倒敘的反覆器,使用 Stack 儲存(後進先出)
      3. 實作 next(),有下一個,就 createIterator() 並放入 Stack
      4. 實作 hasNext(),有下一個回傳 true,沒有就清掉該反覆器
    5. 使用:
      1. 只需要操作葉子 & 合成物件,相當於 葉子 & 節點
      2. 產生 N子物件(兩者不限)
      3. 產生 合成物件,將 N子物件 add 放入
      4. 由底層一層一層加上去

State 狀態模式

  • 目的: 封裝 狀態的行為,依狀態改變行為
  • 定義: 允許物件隨著內在狀態改變而變更行為,好像物件類別改變了一樣
  • 如果 Context 物件消失,State 將會回到初始
  • 作法:
    1. 建立 State 抽象物件,並將所有動作寫成N個方法
    2. 一個狀態就建立一個 concreteState 物件
      1. 吃 Context ,方便呼叫 setState 等方法
      2. handle() 中做完行為,使用 setState(Context.getConcretStatA()) 去變更狀態物件
      3. 當再次執行,會去呼叫儲存在屬性的 State物件中的該動作
    3. 使用:
      1. 建立一個 Context 並保持活著
      2. loop 執行 request()

proxy 代理人模式

  • 目的: 將物件包裝起來,進行存取控制
  • 定義: 讓某個物件擁有替身,藉以控制外界對此物件的接觸
  • 應用範圍舉例
    • Cache Proxy 備用代理:會記住之前 request 的資料,並盡量重複利用
    • Remote Proxy 遠端代理:代理遠端程式執行,如Web Service和WCF、java RMI。
    • Virtual Proxy 虛擬代理:將複雜或耗時實體利用物件替代
    • Protection Proxy 安全代理:控制物件存取權限。ex.CGlib
    • Smart Proxy 智慧參考:提供比原始物件更多服務。
  • 作法:
    1. 依照 RealSubject 架構建立一個 interface
    2. 並讓 Proxy 物件實作,讓別人誤以為 Proxy 就是 RealSubject
    3. 在 Proxy 物件提供 request,必要的時候才會將 request 給 RealSubject 去處理
  • 保護代理人作法:(參考文獻)
    • 使用 java.lang.reflection 中的 InvocationHandler(調用處理器) 介面 & Proxy 物件
    • IH(InvocationHandler 縮寫) 能讓 Proxy & RealSubject 之間有多一層控管機制
    • 可能有N個不同權限的 IH
    • 原本 invoke 是直接寫在 Proxy 物件中有所不同,java proxy 不允許修改,故將其切割成兩塊,IH 另外實作。
    • 作法:
      1. 實踐 IH
        1. 能夠儲存 RealSubject 物件
        2. invoke() ,直接對存放在屬性的 RealSubject 物件做調用
        3. 權限的控管也是寫在此方法內
        4. 吃 RealSubject 物件的方法,回傳 Proxy 物件(Subject介面被套上)
        5. Proxy.newProxyInstance(Subject物件Loader, Subject物件介面, 包含了 RealSubject 的 IH )
      2. 使用:
        1. 將 RealSubject物件 、IH 物件 丟給 Proxy.newProxyInstance 產生一個擁有 Subject 介面的代理人物件
        2. 將回傳的 物件轉型回 RealSubject
        3. 即可依照一般方式使用 RealSubject.request()

Compound 複合模式

剩下的模式

  • Bridge 橋梁模式
  • Builder 建立者模式
  • Chain of Responsibility 責任鏈模式
  • Flyweight 蠅量級模式
  • Interpreter 翻譯者模式
  • Mediator 居間協調者模式
  • Memento 助記物模式
  • Prototype 雛形模式
  • visitor 參觀者模式

模式分類

  • 生成
  • 結構
  • 行為
---------------------------
  • 類別
  • 物件

OO守則

  • 將變動部分封裝起來
  • 多用合成、少用繼承
  • 針對介面寫程式,不是對實踐寫程式
    • 意思是對超型態而寫,超型態可以是: 抽象類別、介面 
  • 鬆綁物件間的緊密度
  • 類別開放擴充、關閉修改
  • 依賴抽象,不依賴具象類別
    • 將原本依賴的具象類別,衍伸出一個抽象類別給予次類別依賴
  • 只告訴你的朋友
  • 別找我,我會找你
  • 類別只有一個理由可改變

參考書籍
Design Pattern Design Pattern Reviewed by Wild on 5/13/2014 10:01:00 上午 Rating: 5

沒有留言:

沒有Google帳號也可發表意見唷!

技術提供:Blogger.