iOS設計模式彙總

Ly夢k發表於2019-01-23

前言

設計模式是有用的抽象化工具,用於解決工程和建築等領域的設計問題。出於同樣的目的,軟體開發領域借用了這一概念,設計模式是一個物件或類的設計模板,用於解決特定領域經常發生的問題。本篇共分8部分涉及22種設計模式:

  • 物件建立(1~6)
    • 原型模式
    • 簡單工廠模式
    • 工廠模式
    • 抽象工廠模式
    • 建造者(生成器)模式
    • 單例模式
  • 介面適配 (7~9)
    • 介面卡模式
    • 橋接模式
    • 外觀模式
  • 物件去耦 (10~11)
    • 中介者模式
    • 觀察者模式
  • 抽象集合 (12~13)
    • 組合模式
    • 迭代器模式
  • 行為擴充套件 (14~16)
    • 訪問者模式
    • 裝飾者模式
    • 責任鏈模式
  • 演算法封裝 (17~19)
    • 模板方法模式
    • 策略模式
    • 命令模式
  • 效能與物件訪問 (20~21)
    • 享元模式
    • 代理模式
  • 物件狀態 (22)
    • 備忘錄模式

Design Partten

物件建立

一、原型模式

  1. 什麼是原型模式?
    使用原型例項指定建立物件的種類,並通過複製這個物件建立新的物件。

原型模式其實是通過一個物件為模板建立另外一個可定製的物件,而且不需要知道任何的建立細節。

  1. 什麼時候使用原型模式?

    • 需要建立的物件應該獨立於其型別和建立方式。
    • 需要例項化的類是在執行時決定的。
    • 不想要與產品層次相對應的工廠層次。
    • 不同類的例項間的差異僅是狀態的若干組合。因此複製相應數量的原型比手工例項化更加方便。
    • 類不容易建立,比如每個元件可把其他元件作為子節點的組合物件。複製已有的組合物件並對副本進行修改會更加容易。
  2. 深拷貝與淺拷貝

    • 淺拷貝,指標級拷貝,拷貝出的新例項依舊指向源記憶體空間,不論修改原來的例項或者拷貝出的例項,都會影響到另一個(因為指標指向同一塊記憶體)。
    • 深拷貝,記憶體級拷貝,會開闢新的記憶體空間,修改原來的例項或者拷貝的例項,另一個不受到影響。

    iOS中預設的-copy是淺拷貝,若要深拷貝,需要遵守<NSCopying>協議,重寫-copyWithZone方法。

二、簡單工廠模式

  1. 什麼是簡單工廠模式?
    簡單工廠模式(Simple Factory Pattern),又稱為靜態工廠方法(Static Factory Method)模式,在簡單工廠模式中,可以根據引數的不同返回不同類的例項。簡單工廠模式專門定義一個類來負責建立其他類的例項,被建立的例項通常都具有共同的父類。
  2. 簡單工廠模式的優點
    • 將物件的建立和物件本身業務處理分離可以降低系統的耦合度,使得兩者修改起來都相對容易。
    • 當你需要什麼,只需要傳入一個正確的引數,就可以獲取你所需要的物件,而無須知道其建立細節。
    • 在呼叫工廠類的工廠方法時,由於工廠方法是靜態方法,使用起來很方便,可通過類名直接呼叫,而且只需要傳入一個簡單的引數即可,修改引數時無須修改任何原始碼。
  3. 簡單工廠模式的缺點
    • 簡單工廠模式最大的問題在於工廠類的職責相對過重,增加新的產品需要修改工廠類的判斷邏輯,這一點與開閉原則是相違背的。

簡單工廠模式Demo(計算器)

三、工廠模式

  1. 什麼是工廠模式?
    定義建立物件的介面,讓子類決定例項化哪一個類。工廠方法使得一個類的例項化延遲到了子類。

  2. 什麼時候使用工廠模式?

    • 編譯時無法準確預期需要建立物件的類。
    • 類想要其子類決定在執行時建立什麼型別的例項。
    • 類有若干輔助類為其子類,而你想將返回哪個子類這種資訊區域性化。
  3. 工廠模式的優勢
    和直接建立具體物件相比,使用工廠方法建立物件算是最佳的做法。
    工廠方法讓客戶端可以要求由工廠方法建立的物件擁有一組共同的行為。
    因此,向類層次結構中引入新的具體產品並不需要修改客戶端程式碼,因為返回的任何具體物件的介面跟客戶端一直使用的介面相同。

  4. Cocoa Touch中的工廠方法
    工廠方法在Cocoa Touch中使用極為廣泛,以NSNumber為例,它提供了很多的建立方法,比如-numberWithBool-numberWithInt他們傳入不同型別的引數,獲得NSNumber例項。

工廠模式Demo(計算器)

四、抽象工廠

  1. 什麼是抽象工廠?
    提供一個建立一系列相關或者相互依賴物件的介面,而無需指定他們的具體類。

    如果有多個類共有相同的行為,但實際實現不同,則可能需要某種抽象型別做為他們的父類,抽取其他們共同的行為到父類中。

    例如,我們知道普通的披薩餅是什麼樣子,在點餐的時候能預計到端上來的是什麼。當我們說"出去吃披薩"時,這裡的“披薩”其實就是一個抽象型別,定義了披薩餅應該共同具有的特徵。但是,從不同的店我們得到同一披薩餅(比如義大利披薩餅、臘腸披薩餅)的味道大不相同。因為有太多型別的披薩餅,我們簡單地將其叫做“披薩餅”以稱呼這種特定型別的食品。

    父類的類方法-getFactory僅僅只是返回具體的(合適需求的)工廠類。其子類不應該過載這個方法。-getFactory方法根據配置返回一個具體的工廠例項。

  2. 抽象工廠和工廠模式的比較
    這兩者在很多方面都很相似,他們都用於相同的目的:建立物件而不讓客戶端知曉建立細節,他們的對比如下:

抽象工廠 工廠模式
建立方式 物件組合建立抽象產品 類繼承建立抽象產品
建立種類 建立多系列產品 建立一種產品
如何擴充套件 修改父類的介面才支援新產品 子類建立者過載工廠方法以建立新的產品

抽象工廠Demo(地圖引擎)

五、建造者(生成器)模式

  1. 什麼是建造者模式 將一個複雜物件的構建與他的表現分離,使得同樣的構建過程可以建立不同的表現。

    它可以將一個產品的內部表象與產品的生成過程分割開來,從而可以使一個建造過程生成具有不同內部表象的產品物件。
    如果我們用了建造者模式,那麼使用者只需要指定需要建造的型別就可以得到他們的物件例項,而無需關心建造過程和細節。

    在此模式中,除了使用者和其所需的產品,還有兩個重要的角色Director(指導者)Builder(生成器)

    • Director知道Builder應該建造什麼,以引數向其提供缺少的資訊來建立特定產品。
    • Builder是一個抽象介面,宣告瞭一個-build方法,該方法由ConcreBuilder實現,以構造實際的產品,ConcreBuilder有一個-getResult方法,向客戶端返回建造完畢的結果。
  2. 何時使用建造者模式

    • 需要建立涉及各種部件的複雜物件。建立物件的演算法應該獨立於部件的裝配方式。常見例子是構建組合物件。
    • 構建過程需要以不同的方式(比如,部件或者表現不同的組合)構建物件。
  3. 建造者模式和抽象工廠的對比
    抽象工廠和建造者模式在 抽象物件建立方有許多相似之處,但是,二者卻大不相同。

    • 建造者模式關注的是分步驟建立複雜物件,很多時候,同一型別的物件可以以不同的方式建立。建造者模式在多步建立過程的最後一步返回產品。
    • 抽象工廠的重點在於建立簡單或者複雜產品的套件。抽象工廠立即返回產品。
    建造者模式 抽象工廠
    象的複雜程度 構建複雜物件
    需要的步驟 多步建立
    建立方式種類 多種方式建立
    返回物件的時機 建立過程的最後一步
    建立結果 專注一個特定的產品
  4. 總結
    生成器模式能幫助構建涉及部件與表現的各種組合的物件。沒有這一模式,知道構建物件所需細節的Director可能會最終變成一個極其複雜的類。帶有無數用於構建同一類的各種表現的內嵌演算法。而這些演算法本應該獨立於該物件的組成部分以及他們的裝配過程。

建造者模式Demo(畫卡通人)

六、單例模式

  1. 何為單例模式

    單例模式:保證一個類僅有一個例項,並且提供一個訪問它的全域性訪問點。

    單例模式幾乎是設計模式中最簡單的了。它的意圖是使得類的一個物件成為系統中的唯一例項。要實現這一點,可以從客戶端對其進行例項化開始。因此,需要用一種只允許生成物件類唯一例項的機制來“阻止”所有想要生成物件的訪問。

  2. 何時使用單例模式

    • 類只能有一個例項,而且必須從一個為人熟知的訪問點對其進行訪問,(比如工廠方法)。
    • 這個唯一的例項只能通過子類化進行擴充套件,而且擴充套件的物件不會破壞客戶端程式碼。
  3. Objective-C實現單例模式
    在OC中,單例模式的實現目前分為兩種:

    • 原始實現
      遵守<NSCopying>協議重寫-alloc方法和-copy方法,考慮執行緒安全。
    • GCD實現
      藉助GCD的dispatch_once實現

單例模式Demo(一個嚴謹的單例)

介面適配

七、介面卡

  1. 何為介面卡模式

    介面卡模式,用於連線兩種不同種類的物件,使其毫無問題地協同工作。其思想比較簡單:介面卡實現客戶端所需要的某種介面的行為,同時,它又連線到一個具有(完全)不同介面行為的物件。一邊是客戶端懂得如何使用的目標介面,另一邊是客戶端一無所知的被適配者,介面卡在二者之間。它的主要作用是把被適配者的行為傳遞給管道另一端的客戶端。

    定義:將一個類的介面轉換成客戶希望的另外一個介面。Adapter模式使得原本介面不相容而不能在一起工作的類可以一起工作了。

  2. 介面卡分類

    • 類介面卡
      它是通過類繼承實現的,而Objective-C有著協議(Protocol)這一語言特性,所以在Objective-C中,類可以實現協議,同時又繼承自父類,從而達到C++中多繼承的效果。
      要在OC中實現類介面卡,首先需要有定義了客戶端要使用的一套行為的協議,然後用具體的介面卡類來實現這個協議,介面卡類同時也繼承被適配者。

    • 物件介面卡
      與上面的類介面卡不同,物件介面卡不繼承被適配者,而是組合了一個對他的引用。

  3. 類介面卡與物件介面卡的比較

    類介面卡 物件介面卡
    只針對單一具體的Adaptee類,把Adaptee適配到target 可以適配多個Adaptee及子類
    易於過載Adaptee的行為,以為是通過直接的子類化進行的適配 難以過載,需要藉助子類的物件而非Adaptee本身
    只有一個Adaptee物件,無需額外的這鎮間接訪問Adaptee 需要額外的指標間接訪問Adaptee並適配其行為

    Adaptee:被適配者
    Target:目標介面

  4. 何時使用介面卡模式

    • 已有類的介面與需求不匹配
    • 想要一個可複用的類,該類能夠同可能帶有不相容介面的其他類協作。
    • 需要適配一個類的幾個不同子類,可是讓每一個子類去子類化一個類適配又不現實,那麼可以使用物件介面卡(委託)來適配其父類的介面。

八、橋接模式

  1. 何為橋接模式?
    將抽象部分與它的實現部分分離,使它們都可以獨立地變化。

    所謂抽象與它的實現分離,這並不是說,讓抽象類與其派生類分離,因為這沒有任何意義。實現指的是抽象類和它的派生類用來實現自己的物件。

    實現系統時可能有多角度分類,每一種分類都有可能變化,那麼就把這種多角度分離出來,讓他們獨立變化,減少他們之間的耦合。

  2. 合成/聚合複用原則

    • 繼承的弊端
      物件的繼承關係在編譯時就已經定好了,所以無法在執行時改變從父類繼承的實現。子類的實現與其父類有著非常緊密的依賴關係,以至於父類實現中任何變化必然會導致子類發生變化。當需要複用子類時,如果繼承下來的實現不適合新的問題,則父類必須重寫或者被其他更適合的類替換。這種依賴關係限制了靈活性並最終限制了複用性。

    • 合成/聚合複用原則
      合成(也叫做組合,Composition)和聚合(Aggregation)都是關聯的特殊種類。聚合表示一種弱的“擁有“關係,體現的是A物件可以包含B物件,但B物件不是A物件的一部分;合成則是一種強的”擁有“關係,體現了嚴格的部分和整體的關係,部分和整體的生命週期一樣。

      比如:大雁有兩個翅膀,翅膀與大雁是部分和整體的關係,並且他們的生命週期相同,於是大雁和翅膀就是合成關係。大雁是群居動物,每隻大雁都屬於一個雁群,一個雁群可以有多隻大雁,所以,大雁和雁群是聚合關係。

    優先使用物件的組合/聚合將有助於保持每個類被封裝,並集中在單個任務上。這樣類和類繼承層次會保持較小規模,並且不太可能增長為不可控制的龐然大物。

  3. 何時使用橋接模式

    • 不想在抽象與實現之間形成固定的繫結關係(這樣可以在執行時按需切換實現)
    • 抽象及其實現都應可以通過子類化獨立擴充套件。
    • 對抽象的實現進行修改不應該影響客戶端程式碼。
    • 如果每個實現需要額外的子類以細化抽象,則說明有必要把他們分成兩個部分。
    • 想在帶有不同抽象介面的多個物件之間共享一個實現。

橋接模式Demo(手機軟體系統)

九、外觀模式

  1. 何為外觀模式
    定義:為系統中的一組介面提供一個統一的介面,外觀定義一個高層介面,讓子系統更易於使用。

    外觀模式為子系統中一組不同的介面提供一個統一的介面。外觀定義了上層介面,通過降低複雜度和隱藏子系統之間的通訊及依存關係,讓子系統更易於使用。

  2. 何時使用外觀模式

    • 子系統正在逐漸變得複雜。應用模式的過程中演化出許多類。可以使用外觀模式為這些子系統類提供一個更簡單的介面。
    • 可以使用外觀對子系統進行分層。每個子系統級別有一個外觀作為入口點。讓他們通過其外觀進行通訊,可以簡化他們的依賴關係。
  3. 總結
    當程式逐漸變大變複雜的時候,會有越來越多小型的類從設計和應用模式中演化出來。如果沒有一種簡化的方式來使用這些類,客戶端程式碼會變得越來越複雜和難以理解,而且難以維護,外觀模式有助於提供一中更為簡單的方法來使用子系統中的這些類。處理這些子系統的預設行為,可能只是定義在外觀中的簡單方法,而不必直接使用這些類。

外觀模式Demo(乘客乘車案例)

物件去耦

十、中介者模式

  1. 何為中介者模式
    定義:用一個物件來封裝一系列物件的互動方式。中介者使各個物件不需要顯示地相互引用,從而使其耦合鬆散,而且可以獨立地改變它們之間的互動

    物件導向的設計鼓勵把行為分散到不同的物件中,這種分散可能導致物件之間的相互關聯。在最糟糕的情況下,所有物件都彼此之間瞭解和相互操作。

    雖然把行為分散到不同物件增強了可複用性,但還是增加的相互關聯有減少了獲得的益處。增加的關聯使得獨享很難或者不能在不依賴其他物件的情況下工作。應用程式的整體行為可能難以改動,因為他分佈於許多物件。

    中介者模式用於解決此類問題,它定義了一個集中的場所,物件之間的互動可以在一箇中介者物件中處理,其他物件不必彼此互動,因此減少了他們之間的依存關係。

  2. 何時使用中介者模式

  • 物件之間的互動雖然定義明確但是非常複雜,導致一組物件彼此相互依賴而且難以理解。
  • 因為物件引用了許多其他物件並且與其通訊,導致物件難以複用。
  • 想要定製一個分佈在多個類中的邏輯或行為,又不想生成太多子類。
  1. 中介者模式的優缺點
    中介者模式很容易在系統中使用,也很容易誤用。當系統出現了“多對多”互動複雜的物件群時,不要急於使用中介者模式,而要先反思系統的設計是否合理。

    • 優點:
      1、中介者的存在,減少了各個具體類的耦合度,使各個具體類和中介者可以獨立地改變和複用;

      2、由於把物件如何協作進行了抽象,將中介者作為一個獨立的概念並將其封裝在一個物件中,這樣關注的物件就從物件各個本身的行為轉移到了他們之間的互動上來,也就是站在一個更巨集觀的角度去看待系統。

    • 缺點:
      由於中介者控制了集中化,於是把互動複雜性變為了中介者的複雜性,這就使得中介者會變得比任何一個具體類都複雜。

  2. 總結
    中介者模式的應用十分廣泛,元件化應該算是最貼切的中介者模式的應用場景了,各個元件,獨立開發,維護,元件之間使用中介軟體進行通訊。詳見:iOS 一個輕量級的元件化思路

    雖然對於處理系統的行為分散於不同物件並且物件相互依存的情況,中介者模式非常有用,但是應該注意避免讓中介者類過於龐大而難以維護。如果已經如此了,可以考慮使用另外的設計模式把它分解。

中介者模式Demo(同事對話)

十一、觀察者模式

  1. 何為觀察者模式
    定義:定義物件間的一種一對多的依賴關係,當一個物件的狀態發生改變時,所有依賴於它的物件都得到通知並且被自動更新

    觀察者模式也叫做釋出——訂閱模式。Observer從Subject訂閱通知。ConcreteObserver實現抽象Observer並過載其update方法。一旦Subject的例項需要通知Observer任何的變更,Subject會傳送update訊息來通知儲存在內部列表中所有註冊的Observer。在ConcreteObserver的update方法的實際實現中,Subject的內部狀態可被取得並在以後進行處理。

  2. 何時使用觀察者模式

    • 有兩種抽象型別相互依賴。將它們封裝在各自的物件中,就可以對它們單獨進行改變和複用。
    • 對一個物件的改變需要同時改變其他物件,而不知道具體有多少物件有待改變。
    • 一個物件必須通知其他物件,而它又不需要知道其他物件是什麼。
  3. Cocoa Touch框架中的觀察者模式

    • 通知
      Cocoa Touch框架中使用NSNotificationCenter和NSNotification物件實現了一對多的釋出——訂閱模式,他們允許主題與觀察者以一種鬆耦合的方式通訊。兩者在通訊時對另外一方無需多少了解。

    • 鍵—值觀察
      Cocoa提供了另外一種稱為鍵-值觀察的機制,物件之間可以通過它得到其他物件特定的變更通知。這種機制在MVC(Model-View-Controller)模式的場景中尤其重要,它讓檢視物件可以經由控制器層觀察模型物件的變更。

      鍵-值觀察
      箇中心物件為所有觀察者提供變更通知 被觀察的物件直接向觀 察者傳送通知
      要從廣義上關注程式事件 繫結於特定物件屬性的值

抽象集合

十二、組合模式

  1. 什麼是組合模式
    定義:將物件組合成樹形結構以表示“部分—整體”的層次結構。組合模式使得使用者對單個物件和組合物件的使用具有一致性。

    組合模式讓我們可以把相同基型別(base type)的物件組合到樹狀結構中,其中父節點包含同型別的子節點。換句話說,這種樹狀結構形成"部分-整體"的層次結構。

    樹形結構是既包含物件的組合(容器)又包含作為葉節點(基元)的單個物件對的一種層次結構。每個組合體包含的節點可以是葉節點也可以是其他組合體。這種關係在這個層次結構中遞迴重複。客戶端對組合結點和葉節點擁有相同的操作,客戶端在使用時可以忽略他們之間的差別。

  2. 何時使用組合模式

    • 想獲得物件抽象的樹形結構(部分-整體層次結構)。
    • 想讓客戶端統一處理組合結構中的所有物件。
  3. 透明方式與安全方式

    • 透明方式
      在葉節點中也有-add-remove方法,然而葉節點上不需要這些行為;這樣做的目的是為了讓他們完全相同的介面,呼叫方完全用處理這種邏輯,這就是透明方式。

    • 安全方式
      如果不希望葉節點上存在上面的方法,那麼在最基本結構(Component)中就不宣告-add-remove方法,而是另外在宣告一個結構(Composite)用來管理子類物件的方法,這樣就能避免透明代理出現的問題,但同時,由於不夠透明,所以樹葉和樹枝擁有不同的介面,客戶端的呼叫需要做相應的判斷,給呼叫方帶來了不便。

    兩種方式沒有絕對的優劣,看個人的理解和取捨(類比簡單工廠和工廠模式)。

組合模式Demo(公司組織架構案例)

十三、迭代器模式

  1. 何為迭代器模式
    定義:提供一種方法順序訪問一個聚合物件中的各個元素,而又不需要暴露獨享的內部表示。

    迭代器提供一種順序訪問聚合物件(集合)中元素的方法,而無需暴露結構的底層表示和細節。遍歷集合中元素的職能從集合本身轉移到迭代器物件。迭代器定義了一個用於訪問集合元素並記錄當前元素的介面。不同迭代器可以執行不同的遍歷策略。

  2. 何時使用迭代器模式

    • 需要訪問組合物件的內容,而又不暴露內部表示。
    • 需要通過多種方式遍歷組合物件。
    • 需要提供一個統一介面,用來遍歷各種型別的組合物件。
  3. Cocoa Touch中的迭代器模式

    • NSEnumerator 從iOS2.0開始,可以使用NSEnumerator來列舉NSArray、NSDictionary、NSSet物件中的元素。
    • 基於block的列舉
      在iOS4中引入了基於塊的列舉(Block-Based Enumeratoration)
    • 快速列舉(for-in)
    • 內部列舉
      NSArry有個例項方法-makeObjectsPerformSelector:,它允許客戶端向陣列中每個元素髮送一個訊息,讓其每個元素執行指定的方法。

迭代器模式Demo(乘客買票案例)

行為擴充套件

十四、訪問者模式

  1. 何為訪問者模式
    定義:表示一個作用於某物件結構中的各元素的操作。它使你可以在不改變各元素類的前提下定義作用於這些元素的新操作。

  2. 何時使用訪問者模式

    • 一個複雜的物件結構包含很多其他物件,他們有不同的介面,這個物件的實施依賴於其具體型別的操作。
    • 需要對一個組合結構體中的物件進行很多不相關的操作,但是不想讓這些操作“汙染”這些物件的類,可以將相關的操作集中起來,定義在一個訪問者類中,並在需要在訪問者中定義的操作時使用它。
    • 定義複雜結構的類很少修改,單經常需要向其新增新的操作。
  3. 總結
    訪問者模式是擴充套件組合結構功能的一種強有力的方式。如果組合結構具有精心設計的基本操作,而且結構相對穩定就可以使用訪問者模式。

訪問者模式Demo(男人女人案例)

十五、裝飾者模式

  1. 何為裝飾著模式
    裝飾者模式:動態地給一個物件新增一些額外的職責。就擴充套件功能來說,裝飾者模式相比生成子類更加靈活。

    Component是定義一個物件介面,可以給這些物件動態地新增職責。ConcreteComponent是定義了一個具體的物件,也可以給這個物件新增一些職責。Decorator,裝飾者抽象類,繼承了Component,從外類來擴充套件Component類的功能,但對於COmponent來說,是無需知道Decorator的存在的。至於ConcreteDecorator就是具體的裝飾物件,起到給Component新增職責的功能。

    在日常開發過程中,應該減少類繼承的使用,過多地使用類的繼承會導致類數目過於龐大而變得難以維護,而使用組合可以讓我們的系統更具彈性,更加容易修改和擴充套件。

  2. 何時使用裝飾者模式

  • 想要在不影響其他物件的情況下,以動態,透明的方式給單個物件新增職責。
  • 想要擴充套件一個類的行為,卻做不到。類定義可能被隱藏,無法進行子類化;或者,對類的每個行為的擴充套件,為支援各種功能組合,將產生大量的子類。
  • 對類的職責的擴充套件是可選的。
  1. 改變物件的“外表”和“內容”

    "外表"變更(裝飾者) “內容”變更(策略)
    從外部變更 從內部變更
    每個節點不知道變更 每個節點知道一組預定義的變更方式

裝飾者模式Demo(給圖片加濾鏡)

十六、責任鏈模式

  1. 何為責任鏈模式

    定義:使多個物件都有機會處理請求,從而避免請求的傳送者和接受者之間發生耦合。此模式將這些物件連城一天鏈,並且沿這條鏈傳遞請求,直到一個物件處理它為止。

    使用責任鏈模式可以隨意地增加或修改處理一個請求的結構,增強了對物件指派職責的靈活性。但是可能一個請求到了責任鏈的末端都得不到處理,或者因為沒有正確地配置而得不到處理,需要考慮全面。

  2. 何時使用責任鏈模式

    • 有多個物件可以處理請求,而處理程式只有在執行時才能確定。
    • 向一組物件發出請求,而不想顯示指定處理請求的特定處理程式。
  3. 總結

    責任鏈模式能很好地解決大量分支判斷,有效降低了客戶端呼叫的邏輯複雜度。以上的3個設計模式,都是在擴充套件物件行為的同時,對物件進行最少修改甚至不修改。對越來越複雜的系統擴充套件具有極大的借鑑意義。

責任鏈模式Demo(加薪案例)

演算法封裝

十七、模板方法模式

  1. 何為模板方法模式

    定義:定義一個操作中的演算法骨架,而將一些步驟延遲到子類中。模板方法使子類可以重新定義演算法的某些y特定步驟而不改變改演算法的結構。

    其基本思想是在抽象類的一個方法中定義“標準”演算法。這個方法的實現由子類過載實現。這個方法被稱為“模板”,因為方法定義的演算法缺少一些特定的操作。子類過載基本操作以提供獨特操作供模板方法使用。

  2. 何時使用模板方法模式

    • 需要一次性實現演算法的不變部分,並將可變的行為留給子類實現。
    • 子類的共同行為應該被提取出來,放到公共類中, 以避免程式碼重複。現有程式碼的差別應該被分離為新的操作。然後用一個呼叫這些新操作的模板方法來替換這些不同的程式碼。
    • 需要控制子類的擴充套件,可以定義一個在特定點呼叫“鉤子(hook)”操作的模板方法。子類可以通過對鉤子的操作實現在這些點擴充套件功能。
  3. 模板方法 vs 介面卡

模板方法 介面卡(委託)模式
父類定義一個一般演算法,但缺少某些特定/可選的資訊或者演算法,它通過這些缺少的資訊或演算法起到一個演算法的“食譜”作用 介面卡與預先定好的委託介面一起定義一個特定的演算法
缺少的資訊有子類通過繼承實現 特定演算法由任何物件通過組合來提供

模板方法模式Demo(製作多種三明治)

十八、策略模式

  1. 何為策略模式
    策略模式定義了演算法家族,分別封裝起來,讓他們之間可以相互替換,此模式讓演算法的變化不會影響到使用演算法的客戶端

    策略模式中的一個關鍵角色是策略類,它為所有支援的相關演算法宣告瞭一個共同介面。另外,還有使用策略介面來實現先關演算法的具體策略類。場景(Context)類的物件配置有一個具體策略物件的例項,場景物件使用策略介面呼叫由具體策略類定義的演算法。

  2. 何時使用策略模式

    • 一個類在其操作中使用多個條件語句來定義許多行為。我們可以把相關的條件分支移到他們自己的策略類中。
    • 需要演算法的各種變體。
    • 需要把複雜的、與演算法相關的資料結構暴露給客戶端。
  3. MVC中的策略模式
    模型-檢視-檢視控制器(MVC)模式中,控制器決定檢視對模型資料的顯示內容和時機。檢視本身知道如何繪圖,但需要控制器告訴他要顯示的內容。一個檢視如果與不同的控制器合作,資料的輸出格式可能一樣,但資料的型別和格式可能隨不同控制器的不同輸出而不同。因此,這種情況下的控制器是檢視的策略。控制器與檢視之間是一種基於策略模式的關係。

  4. 總結
    策略模式和裝飾者模式有些類似。裝飾器從外部擴充套件物件的行為,而各種策略則被封裝在物件之中。所以說裝飾器改變物件的“外表”而策略改變物件的“內容”。

策略模式Demo(商場打折案例)

十九、命令模式

  1. 何為命令模式

    命令物件封裝瞭如何對目標執行指令的資訊,因此,客戶端或呼叫者不必瞭解目標的任何細節,卻仍可以對它執行任何已知的操作。通過吧請求封裝成物件,客戶端可以把它引數化並置入佇列或者日誌中,也能支援撤銷的操作。命令物件將一個或多個動作繫結到特定的接收器。命令模式消除了作為物件的動作和執行它的接收器之間的繫結。

    定義:將請求封裝為一個物件,從而可用不同的請求對客戶端進行引數化,隊請求排隊或記錄請求日誌,以支援可撤銷操作。命令模式把請求一個操作的物件與知道怎麼執行一個操作的物件分割開

  2. 何時使用命令模式

    • 想讓應用程式支援撤銷和恢復。
    • 想用物件引數化一個動作以執行操作,並用不同的命令物件來代替回撥函式。
    • 想要在不同時刻對請求進行指定、排列和執行。
    • 想記錄修改日誌,這樣在系統故障時,這些修改刻在後來重做一遍。
    • 想讓系統支援事務,事務封裝了對資料的一些列修改。事務可以建模為命令物件。
  3. 命令模式的優點

    1. 比較容易地設計一個命令佇列(waiter的commandList陣列)
    2. 在需要的情況下,可以很容易地將命令記錄日誌(在Waiter的setOrder記錄)
    3. 允許接收方命令的一方決定是否否決該命令(Cooker類可以通知Waiter無貨源)
    4. 可以對請求進行撤銷、修改和重做(可以修改命令的數量)
    5. 由於新加的具體命令類不影響其他類,因此增加新的具體命令類很容易

命令模式Demo(燒烤店的訂單操作)

效能與物件訪問

二十、享元模式

  1. 何為享元模式

    定義:運用共享技術有效地支援大量細粒度的物件

    實現享元模式需要兩個關鍵的元件,通常是可共享的享元物件和儲存他們的池。某種中央物件維護這個池,並從它返回適當的物件例項。工廠是這一角色的理想候選,它通過一個工廠方法返回各種型別的具體享元物件。

    享元模式可以避免大量非常相似類的開銷。在程式設計中,有時需要生成大量細粒度的類例項來表示資料。如果能發現這些例項除了幾個引數外基本相同,有時能夠大幅度地減少需要例項化類的數量。如果能把那些引數移到類例項的外面,在方法呼叫時將他們傳遞進來,就可以通過共享大幅度地減少例項的數目。

  2. 何時使用享元模式

    • 應用程式使用很多物件
    • 在記憶體中儲存物件會影響效能
    • 物件的多數特有狀態可以放到外部而輕量化
    • 移除了外在狀態後,可以用較少的共享物件替換原來的那組物件
    • 應用程式不依賴物件標識,因為共享物件不能提供唯一標識

二十一、代理模式

  1. 何為代理模式

    定義:為其他物件提供一種代理以控制對這個物件的訪問。

    代理模式的思想是使用一個基本跟實體物件行為相同的代理,客戶端可以”透明地“使用代理,即不必知道所面對的只是一個代理而不是實體物件。在iOS中,使用代理來解耦合。

    使用代理把View層的事件傳遞到Controller中

    把tableView的delegate和dataSource實現到一個單獨的Model或者ViewModel中

  2. 代理的分類

    • 遠端代理(remote proxy)
      為位於不同地址空間或者網路上的物件提供本地代表
    • 虛擬代理(virtual proxy)
      需要根據需要建立重型物件
    • 保護代理(protection proxy)
      根據訪問許可權控制對原物件的訪問
    • 智慧引用代理(samrt-reference proxy)
      通過對真正物件的引用進行技術來管理記憶體,也用於鎖定真正物件,讓其他物件不能對其進行修改。

八、物件狀態

二十二、備忘錄模式

  1. 何為備忘錄模式

    定義:在不破壞封裝性的前提下,捕捉一個物件的內部狀態,並在改物件之外儲存這個狀態。這樣以後就可以將該物件恢復到原先儲存的狀態。

    備忘錄模式有是三個角色:原發器(originator)、備忘錄(memento)、 管理者(caretaker)

    • 原發器(originator)
      負責建立一個備忘錄Memento,用來記錄當前時刻的內部狀態,並可使用備忘錄恢復內部狀態。originator可根據需要決定Memento儲存originator的內部狀態
    • 備忘錄(memento) 負責儲存originator物件的內部狀態,並防止originator以外的物件訪問備忘錄。
    • 管理者(caretaker)
      負責儲存好備忘錄Memento,不能對備忘錄的內容進行檢查或者修改。
  2. 合適使用備忘錄模式

    • 需要儲存一個物件(或某部分)在某一時刻的狀態,提供以後恢復到這個時刻的狀態。
    • 用於獲取狀態的介面會暴露實現的細節,需要對外隱藏實現細節。

備忘錄模式Demo(遊戲進度儲存於恢復)

相關文章