第6章:可維護性軟件構建方法 6.2可維護性設計模式

大綱

創造性模式算法

  • 工廠方法模式建立對象而不指定要建立的確切類。
  • 抽象工廠模式將具備共同主題的對象工廠分組。
  • Builder模式經過分離構造和表示來構造複雜的對象。

結構模式設計模式

  • Bridge將抽象從其實現中分離出來,這樣二者能夠獨立變化。
  • 代理提供另外一個對象的佔位符來控制訪問,下降成本並下降複雜性。
  • 複合材料組成零個或多個類似的對象,以便它們能夠做爲一個對象進行操做。

*行爲模式數組

  • 中介者能夠經過成爲惟一具備其方法詳細知識的課程,從而實現課堂間的鬆散耦合。
  • Observer是一種發佈/訂閱模式,它容許許多觀察者對象查看事件。
  • 訪問者經過將方法的層次結構移動到一個對象中來將算法從對象結構中分離出來。
  • 責任鏈將命令委託給一系列處理對象。
  • 命令建立封裝動做和參數的對象。

對可重用性和可維護性設計模式的高層考慮緩存

創造性模式

(1) Factory Method pattern

工廠方法模式
也稱爲「虛擬構造器」服務器

意圖:ide

  • 定義一個用於建立對象的接口,但讓子類決定實例化哪一個類。
  • 工廠方法讓類將實例化推遲到子類。

何時應該使用工廠方法? ----當一個類:函數

  • 沒法預測它須要建立的對象的類別
  • 但願它的子類指定它建立的對象
  • 將責任委託給多個幫助者子類中的一個,而且您須要本地化哪些幫助者是委託人的知識。

當客戶不知道要建立哪一個具體類的實例,或者不想在客戶代碼中指明具體建立的實例時,用工廠方法。定義一個用於建立對象的接口,讓其子類來決定實例化哪個類 ,從而使一個類的實例化延遲到其子類。工具

優勢:佈局

  • 消除了將特定於應用程序的類綁定到代碼的須要。
  • 代碼只處理產品接口(Trace),所以它能夠與任何用戶定義的ConcreteProduct(FileTrace,SystemTrace)

潛在的缺點優化

  • 客戶可能必須建立Creator的子類,以便他們能夠建立某個ConcreteProduct。
  • 若是客戶不管如何都要繼承創造者的話,這是能夠接受的,但若是不是,那麼客戶必須處理另外一個進化點。

Open-Closed Principle(OCP) - 對擴展的開放,對修改已有代碼的封閉

(2) Abstract Factory

抽象工廠模式

示例1:考慮一個用戶界面工具包,它支持不一樣操做系統的多個外觀和感受標準:一個UI,包含多個窗口控件,這些控件在不一樣的OS中實現不一樣

  • 如何編寫單個用戶界面,並使這些界面在這些窗口管理器的不一樣外觀和感受標準之間移植?

示例2:考慮支持不一樣控制系統的智能住宅的設施管理系統:一個倉庫類,要控制多個設備,這些設備的製造商各有不一樣,控制接口有差別

  • 如何編寫獨立於製造商的單個控制系統?

抽象工廠模式:提供接口以建立一組相關/相互依賴的對象,但不須要指明其具體類。

名稱:抽象工廠(或工具包)
意圖:容許獨立於實現建立相關對象的家族
方法:使用工廠返回可用於建立相關對象集的工廠。

適用性

  • 獨立於初始化或表示的不一樣系列組件(產品)
  • 必須以互斥和一致的方式使用
  • 隱藏來自客戶的多個家庭的存在
  • 製造商獨立
  • 應對即將到來的變化

筆記

Abstract Factory建立的不是一個完整產品,而是「遵循固定搭配規則的多類產品的實例」,獲得的結果是:多個不一樣產品的對象,各產品建立過程對客戶可見,但「搭配」不能改變。
本質上,Abstract Factory是把多類產品的工廠方法組合在一塊兒

例如

GraphPoet由Word列表和WordNeighborhood列表組成
NetworkTopology由計算機/服務器/路由器列表和NetworkConnection列表組成
換句話說,Vertex和Edge的對象建立密切相關,但不該該是獨立的。 Vertex和Edge的子類,要有固定搭配,不能隨意組合

抽象工廠vs工廠方法

工廠方法僅用於建立一個產品,但抽象工廠用於建立相關或依賴產品系列。

  • 建立一個對象vs建立多個類型的對象

工廠方法模式向客戶端公開建立對象的方法,而抽象工廠則顯示由這些工廠方法組成的一系列相關對象。

  • 一個工廠方法vs多個工廠方法

抽象工廠模式使用組合來將建立對象的責任委派給另外一個類,而工廠方法模式使用繼承並依賴於派生類或子類來建立對象。

  • 使用組合/委派vs使用繼承/子類型

(3) Builder

構造器模式

構造器模式:將複雜對象的構造與其表示分開,以便相同的構建過程能夠建立不一樣的表示。建立複雜對象,包含多個組成部分

  • 複雜對象的構建在多個表示中很常見

示例:將文檔轉換爲多種不一樣的格式

  • 寫出文件的步驟是相同的
  • 每一步的細節取決於格式

就像你在麥當勞點餐同樣!

途徑

  • 構建算法由單個類(「導演」)指定
  • 算法的抽象步驟(每一個部分一個)由接口(「構造器」)指定
  • 每一個表示都提供了界面的具體實現(「實際構造者」)

注意:客戶端要的不是一堆零散的對象(抽象工廠那樣的結果),而是一個完整的產品,客戶端不關心其中的細節組成部分是什麼,如何建立

比較:抽象工廠vs構造器

Abstract Factory建立的不是一個完整產品,而是「遵循固定搭配規則的多類產品實例」,獲得的結果是:多個不一樣產品的實例對象,各產品建立過程對客戶可見,但「搭配」不能改變。

  • 專一於產品系列(相似的產品類型)
  • 不隱藏構造過程

Builder建立的是一個完整的產品,有多個部分組成,客戶不須要了解每一個部分是怎麼建立,各個部分怎麼組合,最終獲得一個產品的完整對象

  • 基礎產品須要構建爲系統的一部分,但建立很是複雜(產品組合)
  • 複雜產品的構造會不時變化
  • 隱藏用戶的建立過程

抽象工廠和構造器能夠很好地協同工做,適用於多種複雜產品系列

例如

GraphPoet由Word列表和WordNeighborhood列表組成
NetworkTopology由計算機/服務器/路由器列表和NetworkConnection列表組成
換句話說,ConcreteGraph的對象建立由Vertex和Edge子類型的對象建立組成。 四個圖應用都要建立圖對象,要爲不一樣應用創建出不一樣的頂點列表和邊列表。

比較:模板方法vs構造器

模板方法:行爲模式,目標是爲了複用算法的公共結構(次序)

  • 定義了一個操做中算法的骨架(步),而將具體步驟的實現延遲到子類中,從而複用算法的結構並可從新定義算法的某些特定步驟的實現邏輯。
  • 複用算法骨架,強調步驟的次序 - 子類override算法步驟

Builder:一種創造模式,目標是「建立複雜對象」,靈活擴展

  • 將一個複雜對象的構造方法與對象內部的具體表示分離出來,一樣的構造方法能夠創建不一樣的表現。
  • 不強調複雜對象內部各部分的「次序」
  • 子類override複雜對象內部各部分的「建立」
  • 適應變化:經過派生新的builder來構造新的對象(即新的內部表示),OCP

結構模式

(1) Bridge

橋接模式

適用性

  • 用不一樣的實現去耦合抽象概念
  • 實施可能會在運行時切換
  • 實施變動不該影響客戶
  • 從客戶端隱藏類的界面

結構:使用兩個層次結構

  • 適合客戶的邏輯
  • 爲不一樣的實現物理一個

對象:提升可擴展性

  • 邏輯類和物理類獨立變化
  • 隱藏來自客戶的實施細節

橋接模式是OOP最基本的結構模式,經過委託+繼承創建兩個具體類之間的關係(DIP依賴轉置,抽象依賴於抽象)

橋接模式與策略模式

橋接:結構模式,強調雙方的運行時委派鏈接

  • 結構模式代表對象組成或關聯或繼承以造成更大對象的方式,即它們關注對象組成。

一個類A的對象中有其餘類B的對象做爲其組成部分,但A的對象具體綁定到B的哪一個具體子類的實現?在運行時經過委託加以組合,並永久保存這種代理關係。

  • 它將抽象和實現分離開來,並容許二者獨立地變化。

策略:行爲模式,強調一方運行時使用另外一方的「算法」

  • 行爲模式處理算法或業務邏輯(而不是對象建立自己),即它們專一於對象之間的協做。
  • 它使您可以在運行時在一系列算法中切換多種算法。

「算法」一般實現爲「類的某個方法」的形式,策略模式的目的並不是在「調用算法的類」與「被調用算法所在的類」之間創建起永久聯繫,而只是幫助前者臨時使用後者中的「算法」,前者無需永久保存後者的實例。

策略:使用螺絲刀的時候,針對不一樣的工做任務,選取不一樣的「刀頭」,但目的並不是將螺絲刀與刀頭組合起來創建永久的團隊,而只是臨時經過委派完成任務(即調用刀頭的「算法」),而後兩者再無聯繫。

橋接:類A須要完成「通信」功能,它有一個組成部分Device類,這是個抽象接口(模式中的實現者),有多種實現方式(PC,tablet,phone,即ConcreteImplementor),該模式在運行時爲類A的具體子類型實例設定具體的Device的具體實現(強調對A和Device兩個抽象類的各自實例之間如何創建委託),進而在其餘各項功能中利用其通信功能(這再也不是橋接模式關注的目標)。

(2) Proxy

代理模式

代理模式動機

目標:

  • 防止客戶直接訪問對象
  • 經過充當傳遞實體或佔位符對象來容許進行對象級訪問控制。

解決方案:

  • 使用一個稱爲代理的附加對象
  • 客戶端只能經過代理訪問受保護的對象
  • 代理跟蹤受保護對象的狀態和/或位置

某個對象比較「敏感」/「私密」/「貴重」,不但願被客戶端直接訪問到,故設置代理,在兩者之間創建防火牆。

代理模式:3種類型

信息緩存(「遠程代理」)

  • 代理對象是不一樣地址空間中對象的本地代理
  • 若是信息沒有常常變化,那麼很好

Standin(「虛擬代理」)

  • 對象建立成本過高或者下載成本過高。
  • 若是真正的對象不被太頻繁地訪問,那麼很好

訪問控制(「保護代理」)

  • 代理對象爲真實對象提供保護
  • 好的時候,不一樣的演員應該對同一個對象有不一樣的訪問和查看權限
  • 示例:管理員,教師和學生訪問的成績信息。

代理與適配器

適配器:結構模式,目的是將類/庫A的接口更改成客戶端B的指望。
目的:消除不兼容,目的是B以客戶端指望的統一的方式與A創建起聯繫。

  • 典型的實現是一個包裝類或一組類。
  • 目的不是爲了方便未來的界面更改,而是當前的界面不兼容。

代理:行爲模式,也使用包裝類,但其目的是爲實際資源建立一個替身。
目的:隔離對複雜對象的訪問,下降難度/代價,定位在「訪問/使用行爲」

  • 真正的資源駐留在遠程計算機上(代理方便與遠程資源的交互)
  • 真正的資源建立成本很高(代理確保成本不會發生,除非/直到真正須要)
  • 代理提供了替代它所表明的真實資源的插入替代品,所以它必須提供相同的接口。

3) Composite

組合模式

問題:

  • 應用程序須要操做「原始」和「複合」對象的分層集合。
  • 原始對象的處理是單向處理的,處理複合對象的處理方式不一樣。
  • 在嘗試處理每一個對象以前必須查詢每一個對象的「類型」是不可取的。

意圖:將對象組成樹結構以表示總體部分層次結構。

  • 組合模式讓客戶能夠統一處理單個物體和物體的組合。
  • 遞歸組合
  • 「目錄包含條目,每一個條目均可以是目錄。」
  • 一對多「has a」之上「is a」層次

組合策略

包含菜單項的菜單,每一個菜單項均可以是菜單。
包含小部件的行列GUI佈局管理器,每一個小部件均可以是行列GUI佈局管理器。
包含文件的目錄,每一個文件均可以是一個目錄。
包含元素的容器,其中每一個容器均可以是容器。
樹結構

複合vs裝飾器

複合:結構模式,容許您以容許外部代碼將整個結構視爲單個實體的方式構建分層結構(樹)。目的是在同類型的對象之間創建起樹型層次結構,一個上層對象可包含多個下層對象

  • 葉實體的接口與複合實體的實體徹底相同。
  • 複合結構中的全部元素都具備相同的界面,即便有些元素是葉節點,而其餘元素是整個結構。

裝飾器:結構模式,也容許一個實體徹底包含另外一個實體,以便使用裝飾器看起來與包含的實體相同。強調的是同類型對象之間的「特性增長」問題,它們之間是平等的,區別在於「擁有特性」的多少,每次裝飾只能做用於一個對象。

  • 這容許修飾器修改其封裝的行爲或內容,而不改變實體的外觀。

行爲模式

(1) Observer

觀察者模式

問題:依賴的狀態必須與主人的狀態一致

解決方案:定義四種對象:

  • 摘要主題:保留家眷名單; 當主人更改時通知他們
  • 抽象觀察者:定義更新依賴者的協議
  • 具體主題:爲受撫養人管理數據; 當主人更改時通知他們
  • 具體的觀察者:在收到更新消息後得到新的主體狀態

「粉絲」對「偶像」感興趣,但願隨時得知偶像的一舉一動
粉絲到偶像那裏註冊,偶像一旦有新聞發生,就推送給已註冊的粉絲(回調粉絲的特定功能)

建模對象之間的一對多依賴關係

  • 鏈接觀察對象的狀態,主體與許多觀察對象,觀察者

用法:

  • 保持冗餘狀態的一致性
  • 優化一批更改以保持一致性

保持一致性的三個變體:

  • 推送通知:每當主題狀態改變時,全部觀察者都會收到通知
  • 推送更新通知:主題還將已更改的狀態發送給觀察者
  • 拉通知:觀察者詢問對象的狀態

也稱爲發佈 - 訂閱(Publish-ubscribe)。

優勢:

  • 主體和觀察者之間的低耦合:主體不知道家眷
  • 支持廣播:動態添加和刪除觀察員
  • 意外更新:由觀察者在計算上不受主題的控制

實施問題

  • 存儲觀察員列表:一般在主題中
  • 觀察多個主題:一般將參數添加到update()
  • 誰觸發更新:主體的狀態設置操做

(2) Visitor

訪客模式

訪問者模式:容許在運行時將一個或多個操做應用於一組對象,將操做與對象結構分離。 對特定類型的對象的特定操做(訪問),在運行時將兩者動態綁定到一塊兒,該操做能夠靈活更改,無需更改被訪問的類

  • Visitor模式實際上作的是建立一個使用其餘類中的數據的外部類。
  • 若是操做邏輯改變了,那麼咱們只須要在訪問者實現中進行更改,而不是在全部項目類中進行更改。

本質上:將數據和做用於數據上的某種/些特定操做分離開來。

訪客與迭代器

迭代器:行爲模式,用於順序訪問彙集,而不暴露其基礎表示。 因此你能夠在一個Iterator後面隱藏一個List或數組或相似的聚合。
迭代器:以遍歷的方式訪問集合數據而無需暴露其內部表示,將「遍歷」這項功能委託到外部的迭代器對象。

訪問者:行爲模式,用於對元素結構執行操做而不改變元素自己的實現。
在特定ADT上執行某種特定操做,但該操做不在ADT內部實現,而是委託到獨立的訪客對象,客戶端可靈活擴展/改變訪問者的操做算法,而不影響ADT

策略vs訪客

訪客:行爲模式
策略:行爲模式

兩者都是經過受權創建兩個對象的動態聯繫

  • 可是Visitor強調是的外部定義某種對ADT的操做,該操做於ADT自身關係不大(只是訪問ADT),故ADT內部只須要開放accept 訪客)便可,客戶端經過它設定訪客操做並在外部調用。
  • 而策略是強調是對ADT內部某些要實現的功能的相應算法的靈活替換。這些算法是ADT功能的重要組成部分,只不過是委託到外部戰略類而已。

區別:訪客是站在外部客戶的角度,靈活增長對ADT的各類不一樣操做(哪怕ADT沒實現該操做),策略是站在內部ADT的角度,靈活變化對其內部功能的不一樣配置。

(3) Mediator

中介模式

使用中央控制器是中介模式的關鍵方面。

  • 機場控制塔照看能夠起飛的航班,而且全部的通訊都是從飛機到控制塔完成的,而不是進行飛機與飛機之間的通訊。 多個對象之間要進行交互,不是直接交互,而是經過mediator,實現消息的廣播,從而將對象之間鬆散耦合,利於變化

經過封裝不一樣對象集相互交互和通訊的方式,容許鬆耦合。

  • 容許每一個對象的操做彼此獨立地變化。

觀察者模式與中介模式

觀察者模式:定義對象之間的一對多依賴關係,以便當一個對象改變狀態時,它的全部依賴項都會被自動通知和更新。
一組對象對另外一個對象B的狀態變化感興趣(1對多),因此對其進行觀察.B維持着一個對本身感興趣的對象列表,一旦本身發生變化,就通知這些對象。並不對等,一方只是「通知」,另外一方接到通知後執行特定行爲。

中介模式:定義一個封裝一組對象如何交互的對象。介體經過讓對象明確地互相引用來促進鬆散耦合,而且可讓您獨立地改變它們的相互做用。
一組同類型的對象,彼此之間相互發/收消息(多對多),不存在誰觀察誰的問題,全部對象都對等。每一個對象都維持一箇中介,將通信的任務委託給它,本身只負責發送和接收便可,無需瞭解其餘物體,實現了「解耦」。

觀察者模式:本身來廣播,其餘對象接收;
中介模式:第三方中介負責廣播(相似於「郵件列表」)。

(4) Command

命令

意圖

  • 將請求封裝爲一個對象,從而讓您用不一樣的請求參數化客戶端,排隊或記錄請求,並支持可撤銷操做。

將「指令」封裝爲對象,指令的全部細節對客戶隱藏,在指令內對具體的ADT發出動做(調用ADT的細節操做)

  • 將「調用對象的方法」提高爲徹底對象狀態
  • 面向對象的回調

問題

  • 須要向對象發出請求,而不知道請求的操做或請求的接收者。

客戶端但願執行指令,但不想知道指令的細節,也不想知道指令的具體做用對象

命令將調用操做的對象與知道如何執行操做的對象分離。

  • 爲了實現這種分離,設計人員建立了一個抽象基類,它將接收者(對象)與動做(指向成員函數的指針)進行映射。 基類包含一個execute()方法,它只是簡單地調用接收方的動做。

每當客戶端須要對象的「服務」時,Command對象的全部客戶端都經過簡單地調用對象的虛擬execute()方法將每一個對象視爲「黑盒子」。
將全部對客戶提供的指令與內部執行的ADT操做完全分開,指令對外看來是「黑盒」 - 進一步「變本加厲」的封裝!

一個Command類包含如下的一些子集:

  • 一個對象,一個應用於該對象的方法,以及該方法應用時傳遞的參數。
  • 命令的「執行」方法會致使碎片彙集在一塊兒。

外觀與命令

外觀:結構模式
命令:行爲模式

均強調對某個複雜系統內部提供的功能的「封裝」,對外提供簡單的調用接口,簡化客戶端的使用,「隱藏」細節。
命令:強調將指令封裝爲了「對象」,提供了統一的對外接口(執行)。
外觀:沒有顯式的「對象」,仍然經過類的方法加以調用。

(5) Chain of responsibility

責任鏈模式

問題

  • 可能有多個「處理程序」或「處理元素」或「節點」對象,以及必須處理的請求流。

針對一個請求,可能有多個處理模塊

  • 須要高效地處理請求,而無需硬連線處理程序關係和優先級或請求處處理程序映射。

各類不肯定狀況存在,不能以「硬編碼」的方式指明按何種次序調用處理模塊
意圖

  • 經過給予多個對象機會來處理請求,避免將請求的發送者耦合到它的接收者。
  • 連接接收對象並沿着鏈傳遞請求,直到對象處理它爲止。 使用包含許多可能的處理程序的單個處理管道啓動和離開請求。 具備遞歸遍歷的面向對象的鏈表。

避免在請求方和各處理者之間的緊耦合:構造流水線,請求在其上傳,直到被處理爲止。

將處理元素封裝在「管道」抽象中; 並讓客戶在管道入口處「啓動並離開」他們的請求。
客戶端只需在流水線的入口發出請求便可,請求自動在流水線上傳遞,直到被處理。

該模式將接收對象連接在一塊兒,而後將任何請求消息從對象傳遞到對象,直到它到達可以處理消息的對象。

  • 處理程序對象的數量和類型事先是未知的,它們能夠動態配置。

處理器對象的數據和類型事先都不肯定,須要動態配置

  • 連接機制使用遞歸組合來容許無限數量的處理程序進行連接。

使用遞歸組合的方式,將多個處理程序連成「職責鏈」
責任鏈簡化了對象互連。

  • 發件人和收件人不是保留對全部候選收件人的引用,而是每一個發件人對鏈的頭部保持一個單一的引用,而且每一個收件人對鏈中的其直接後繼者保持單一引用。

請求的發起者無需維護全部可能的處理程序對象,只需記錄職責鏈的入口;每一個處理程序只須要維護「下一個」處理程序便可,從而簡化了各個處理程序之間的鏈接關係。

訪客與責任鏈

不是像傳統類設計中將操做與數據捆綁在一塊兒造成ADT,這兩個設計模式都是將「數據」與「做用於數據上的客戶端定製操做」分離開來
緣由:操做能夠靈活增長,運行時使用的操做能夠動態配置,多個操做的執行次序能夠動態變化

區別1:visitor只定義了一個操做,chain of responsibility定義了一組操做及其之間的次序
區別2:訪客中,客戶建立訪客以後傳入ADT,ADT再將執行權委託到visitor;責任鏈中,控制權徹底在各個處理程序之間流轉,ADT(請求對象)徹底感覺不到。

可重用性和可維護性設計模式的高層考慮

使用設計模式的線索(1)

文本:「獨立於製造商」,「獨立於設備」,「必須支持一系列產品」
=>抽象工廠模式
文本:「必須與現有對象接口」
=>適配器模式
文本:「必須與多個系統接口,其中一些系統將在將來開發」,「必須展現早期的原型」
=>橋模式
文本:「必須與現有的一組對象接口」
=>外牆模式

使用設計模式的線索(2)

文本:「複雜結構」,「必須具備可變的深度和寬度」
=>複合模式
文本:「必須是位置透明的」
=>代理模式
文本:「必須可擴展」,「必須可擴展」
=>觀察者模式
文本:「必須提供獨立於該機制的政策」
=>策略模式

總結

組合,適配器,橋接,包裝,代理(結構模式)

  • 重點:組成物體造成更大的結構
    •從舊功能中實現新功能,
    •提供靈活性和可擴展性

命令,觀察者,策略,模板(行爲模式)

  • 重點:算法和對象的責任分配
    •避免與特定解決方案緊密耦合

抽象工廠,生成器(創造性模式)

  • 重點:建立複雜的對象
    •隱藏複雜對象的建立和組合

總結

創造性模式

  • 工廠方法,抽象工廠,Builder

結構模式

  • 橋接,代理,複合

行爲模式

  • 中介,觀察員,訪問者,責任鏈,命令

對可重用性和可維護性設計模式的高層考慮

相關文章
相關標籤/搜索