23個經典設計模式的Swift實現

前言

這是一篇主觀的文章,文字解釋也儘量簡單,寫做目的是一次團隊內的知識分享,想讓不瞭解設計模式的同事迅速對這些生詞混個臉熟。因此本文適合懂Swift語法,想快速瞭解23個設計模式大概在講什麼的同窗。html

前置知識 UML那些線java

基本結構

  • 比喻 讓我聯想到的一些事物
  • 官方定義 原版定義
  • UML 不是原版UML 只保留了我以爲核心的部分
  • 代碼 Swift實現,這個是本體
  • 講解 假設已經看過代碼的一些零散評註

目錄

  • Creational 建立型 5
    • Abstract Factory 抽象工廠模式
    • Builder 建造者模式
    • Factory Method 工廠方法模式
    • Prototype 原型模式
    • Singleton 單例模式
  • Structural 結構型 7
    • Adapter 適配器模式
    • Bridge 橋接模式
    • Composite 組合模式
    • Decorator 裝飾者模式
    • Facade 外觀模式
    • Flyweight 享元模式
    • Proxy 代理模式
  • Behavioral 行爲型 11
    • Chain of responsibility 責任鏈模式
    • Command 命令模式
    • Interpreter 解釋器模式
    • Iterator 迭代器模式
    • Mediator 中介模式
    • Memento 備忘錄模式
    • Observer 觀察者模式
    • State 狀態模式
    • Strategy 策略模式
    • Template Method 模板方法模式
    • Visitor 訪問者模式

工廠模式

工廠模式顧名思義,就像一個工廠生產你所須要的產品算法

配圖:http://cdn1.alphr.com/sites/alphr/files/2016/02/tesla_factory_tour_1.jpg

無工廠 Non-Factory

也就是工廠問題想解決的原始問題。數據庫

protocol Product {}
class ConcreteProductA: Product {}
class ConcreteProductB: Product {}

class Client {
    func createProduct(type: Int) -> Product {
        if type == 0 {
            return ConcreteProductA()
        } else {
            return ConcreteProductB()
        }
    }
}

let c = Client()
c.createProduct(type: 0) // get ConcreteProductA
複製代碼

從代碼和UML能夠看出,爲了獲得產品A,調用者Client要同時依賴Product, ConcreteProductAConcreteProductB,並親自寫一個建立產品的方法。編程

每當需求新增一個產品,就要改動到調用方Client。若是這一堆建立代碼若是能夠抽離出去就行了,因而簡單工廠出現了。swift

簡單工廠 Simple Factory

簡單工廠就作了一件事,把Client要作的建立工做,挪到了另外一個類裏設計模式

protocol Product {}
class ConcreteProductA: Product {}
class ConcreteProductB: Product {}

class Client {
    let s = Factory()
}

class Factory {
    func createProduct(type: Int) -> Product {
        if type == 0 {
            return ConcreteProductA()
        } else {
            return ConcreteProductB()
        }
    }
}

let c = Client()
c.s.createProduct(type: 0) // get ConcreteProductA
複製代碼

Factory代替了Client對具體Product的依賴,那麼當需求變化的時候,咱們再也不須要改動調用方。這當然有所進步,但沒法避免的是,每次變更都要在createProduct的方法內部新增一個if-else分支,這顯然違背了開閉原則緩存

爲了解決這個問題,咱們引入另外一個模式。安全

工廠方法 Factory Method

官方定義markdown

定義一個建立對象的接口,讓其子類本身決定實例化哪個工廠類,工廠模式使其建立過程延遲到子類進行。

protocol Product {}
class ConcreteProductA: Product {}
class ConcreteProductB: Product {}

class Client {
    let f = Factory()
}

class Factory {
    func createProduct() -> Product? { return nil } // 用於繼承
    func createProduct(type: Int) -> Product? { // 用於調用
        if type == 0 {
            return ConcreteFactoryA().createProduct()
        } else {
            return ConcreteFactoryB().createProduct()
        }
    }
}

class ConcreteFactoryA: Factory {
    override func createProduct() -> Product? {
        // ... 產品加工過程
        return ConcreteProductA()
    }
}

class ConcreteFactoryB: Factory {
    override func createProduct() -> Product? {
        // ... 產品加工過程
        return ConcreteProductB()
    }
}

let c = Client()
c.f.createProduct(type: 0) // get ConcreteProductA
複製代碼

對於工廠方法的實現,有衆多不一樣的解法,好比Factory只保留一個createProduct讓子類實現,讓Client來選擇生成哪一個具體工廠實例;或是引入一個FactoryMaker的中間層,做爲生產工廠的「簡單工廠」。我這裏採用的方式是Factory既做爲工廠父類,讓具體工廠決定生產生麼產品,又做爲接口類,讓Client能夠經過依賴注入選擇特定工廠。我這樣作的目的是,在不引入新的中間層的狀況下,最小化Client的依賴。

工廠方法在簡單工廠的基礎上作了兩件事:

  • 多了一層抽象,把生產產品的工做延遲到子類執行。
  • 把「選擇如何生產產品的工做」轉化爲「選擇讓哪一個具體工廠生產」。

工廠方法的貢獻在於,這樣作雖然不能完美避免對一個if-else的擴展,可是這個擴展規模被極大限制住了(只須要new一個類)。

工廠方法着重點是解決了單一產品線的派生問題。那若是有多個相關產品線呢?

抽象工廠 Abstract Factory

官方定義

提供一個建立一系列相關或相互依賴對象的接口,而無需指定它們具體的類。

protocol ProductA {}
class ConcreteProductA1: ProductA {}
class ConcreteProductA2: ProductA {}

protocol ProductB {}
class ConcreteProductB1: ProductB {}
class ConcreteProductB2: ProductB {}

class Client {
    let f = Factory()
}

class Factory {
    func createProductA() -> ProductA? { return nil } // 用於繼承
    func createProductB() -> ProductB? { return nil } // 用於繼承
    func createProductA(type: Int) -> ProductA? { // 用於調用
        if type == 0 {
            return ConcreteFactory1().createProductA()
        } else {
            return ConcreteFactory2().createProductA()
        }
    }
    func createProductB(type: Int) -> ProductB? { // 用於調用
        if type == 0 {
            return ConcreteFactory1().createProductB()
        } else {
            return ConcreteFactory2().createProductB()
        }
    }
}

class ConcreteFactory1: Factory {
    override func createProductA() -> ProductA? {
        // ... 產品加工過程
        return ConcreteProductA1()
    }
    override func createProductB() -> ProductB? {
        // ... 產品加工過程
        return ConcreteProductB1()
    }
}

class ConcreteFactory2: Factory {
    override func createProductA() -> ProductA? {
        // ... 產品加工過程
        return ConcreteProductA2()
    }
    override func createProductB() -> ProductB? {
        // ... 產品加工過程
        return ConcreteProductB2()
    }
}

let c = Client()
c.f.createProductA(type: 0) // get ConcreteProductA1
c.f.createProductA(type: 1) // get ConcreteProductA2
c.f.createProductB(type: 0) // get ConcreteProductB1
c.f.createProductB(type: 1) // get ConcreteProductB2
複製代碼

圖很嚇人,其實很簡單。

當咱們有兩個相關的產品線ProductAProductB, 例如螺絲和螺母,他們派生出ProductA1ProductB1ProductA2ProductB2,前者咱們由工廠ConcreteFactory1來製做,後者由 ConcreteFactory2來製做。

對於Client來講,他只須要知道有一個抽象的工廠能同時生產ProductAProductB就好了,那就是圖中的Factory。

重點來了,這個抽象的Factory是經過「工廠方法」模式把構造過程延遲到子類執行的,也就是說,抽象工廠是創建在工廠方法的基礎上的模式。

因此抽象工廠,換句話說,就是多個產品線須要綁定在一塊兒,造成一個抽象的綜合工廠,由具體的綜合工廠來批量實現「工廠方法」的一種更「高級」的模式。

總結

有點繞,說完這些感受我已經中文十級了。總之,我想表達的觀點是:這些工廠模式並非割裂的存在,而是一個遞進的思想


Builder 建造者模式

建造者模式就像你委託一個室內設計師裝修你的新家

配圖: http://tse2.mm.bing.net/th?id=OIP.N3hIhcOq32Bh6Ezi6q6kGwHaFj&pid=Api

官方定義

將一個複雜的構建與其表示相分離,使得一樣的構建過程能夠建立不一樣的表示。

若是說以前的談到的工廠模式是把建立產品(對象)的工做抽離出去的話,此次要聊的建造者模式,就是把產品內部的組件生產工做抽離出去。這樣作的場景,適用於那些有着複雜、規則模塊的對象生成流程。換句話說,工廠模式是一個類(工廠)建立另外一個類(產品),而建造者是一個類(產品)自身的屬性(組件)構造過程。

對於建造者模式的實現網上也是版本不一:複雜點的版本會引入一個Director的角色,作一個總體上下文,組裝更傻瓜化的builder和product。或是抽象一層Builder協議,用不一樣的具體Builder來構造不一樣的產品。但我認爲這些都模糊了這個模式要傳達的焦點,對理解沒有幫助,因此這裏我選擇一個極簡的模型。

struct Builder {
    var partA: String
    var partB: String
}

struct Product {
    var partA: String
    var partB: String
    init(builder: Builder) {
        partA = builder.partA
        partB = builder.partB
    }
}

// 經過builder完成產品建立工做
let b = Builder(partA: "A", partB: "B")
// 這樣產品只須要一個builder就能夠完成製做
let p = Product(builder: b)
複製代碼

咱們讓Product的生成由本身發起,可是它的組件(屬性)全都委託給Builder來實現,而它只須要依賴一個Builder就完成了自身的生產工做。


Prototype 原型模式

原型模式讓你有了一個能夠源源不斷自我賦值的類。

配圖: http://thumbs.dreamstime.com/z/cell-division-two-cells-divide-osmosis-background-other-cells-48181492.jpg

官方定義

用原型實例指定建立對象的種類,而且經過拷貝這些原型建立新的對象。

原型模式很簡單,你只要實現一個返回你本身的新對象的方法便可。這裏我採用的實現還不是最簡單的,這個interface並非必須的。

原型模式實現了深拷貝。

protocol Prototype {
    func clone() -> Prototype
}

struct Product: Prototype {
    var title: String
    func clone() -> Prototype {
        return Product(title: title)
    }
}

let p1 = Product(title: "p1")
let p2 = p1.clone()
(p2 as? Product)?.title // OUTPUT: p1
複製代碼

Singleton 單例模式

單例就像一個公司的IT部門,他們是惟一的存在,而且被全部人直接訪問。

配圖:The IT Crowd

官方定義

保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。

由於第二點經常被忽視,因此過分使用的危害極大,你無從知道調用從何而來,這種goto通常的存在會變成維護的噩夢。

單例比較常見的應用是例如數據庫,網絡框架的全局訪問點。

單例其實就是變種的原型模式,只不過原型每次返回的是一個拷貝。

Swift的簡單實現:

class Singleton {
    static let sharedInstance = Singleton()
    private init() {
        // 用private防止被new
    }
}
let s  = Singleton.sharedInstance
let s2 = Singleton() // ERROR: initializer is inaccessible due to 'private' protection level
複製代碼

Swift的完整實現:

class Singleton {
    static var singleton: Singleton? = nil
    private init() {}
    static func sharedInstance() -> Singleton {
        if singleton == nil {
            singleton = Singleton()
        }
        return singleton!
    }
}

let s = Singleton.sharedInstance()
let s2 = Singleton() // ERROR: initializer is inaccessible due to 'private' protection level
複製代碼

Adapter 適配器模式

適配器就像一個電源轉換插頭。

配圖: http://www.wap135.com/270/timg08/uploaded/i8/TB2452gtpXXXXaLXXXXXXXXXXXX_!!622114048.jpg

官方定義

將一個類的接口轉換成客戶但願的另一個接口。適配器模式使得本來因爲接口不兼容而不能一塊兒工做的那些類能夠一塊兒工做。

一個類能稱之爲適配器,就是指它能把另外一個類進行某種變形,讓其能和實際需求對接。 好比一個底層數據模型,能夠經過不一樣的UI適配器,對應不一樣的展示須要。

protocol Target {
    var value: String { get }
}

struct Adapter: Target {
    let adaptee: Adaptee
    var value: String {
        return "\(adaptee.value)"
    }
    init(_ adaptee: Adaptee) {
        self.adaptee = adaptee
    }
}

struct Adaptee {
    var value: Int
}

Adapter(Adaptee(value: 1)).value // "1"
複製代碼

Bridge 橋接模式

橋接模式就是這麼一座橋,它矗立在具體和抽象之間,當你調用的時候只看到了抽象,可是它內部實現時「橋接」到了具體。

配圖: https://refactoring.guru/design-patterns/bridge

官方定義

將抽象部分與實現部分分離,使它們均可以獨立的變化。

橋接模式是繼工廠模式以後另外一個有點繞的概念,爲了避免一言不合就拋概念把你們繞暈,咱們直接來看一下這個模式最終成型的樣子。

首先假設咱們封裝了一個「開關能力」的接口。

protocol 開關能力 {
    func turnOn(_ on: Bool)
}
複製代碼

這個接口只有一個方法,實現者須要提供打開/關閉開關會觸發什麼操做。

而後咱們抽象一個叫「設備」的類出來,這個類就有意思了:

  • 首先他有一個實現了開關能力的實例變量,並經過初始化賦好值。
  • 他有一個方法的實現就是直接提供了開關能力裏的實現。
class 設備 {
    let obj: 開關能力
    func turnOn(_ on: Bool) {
        obj.turnOn(on)
    }
    init(_ obj: 開關能力) {
        self.obj = obj
    }
}
複製代碼

這樣,其實一個橋接模式就已經搭建完了。再講解以前,咱們直接看一下如何應用它:

class 電視: 開關能力 {
    func turnOn(_ on: Bool) {
        if on {
            // 打開電視
        } else {
            // 關閉電視
        }
    }
}

class 空調: 開關能力 {
    func turnOn(_ on: Bool) {
        if on {
            // 打開空調
        } else {
            // 關閉空調
        }
    }
}

let tv = 設備(電視())
tv.turnOn(true) // 打開電視

let aircon = 設備(空調())
aircon.turnOn(false) // 關閉空調
複製代碼

經過這段代碼能夠看出:

  • 在把抽象的設備應用到具體的業務的時候,這個模式採用的是組合了一個實現了開關能力接口的實例,而沒用繼承。
  • 最終調用的時候,是由統一的設備做爲接入點的,而不是電視空調這些具體類,具體的實現是經過組合的方式注入設備裏的。

瞭解了這個流程後,一個事情就明朗了:

這不就是在用組合代替繼承嗎?

沒錯,它把須要變化的代碼經過接口代理出去,而避免了繼承。

可是,橋接模式的在哪裏?

這時,要搬出他的概念了: Bridge pattern - Wikipedia

The bridge pattern is a design pattern used in software engineering which is meant to "decouple an abstraction from its implementation so that the two can vary independently". 橋接模式解耦了抽象和具體,讓它們能夠獨立變化。

從代碼去尋找具體和抽象,就能夠發現:

這座橋能走通的關鍵就是這個組合在抽象類裏的變量。

最後,你會發現,這不就是Adapter和Adaptee嗎? 沒錯,設計模式實際上是連續劇來着。

不一樣在於適配器的關注點是如何讓兩個不兼容的類對接, 而橋接模式關注點是解耦


Composite 組合模式

組合模式就像一個公司的組織架構,存在基層員工(Leaf)和管理者(Composite),他們都實現了組件(Component)的work方法,這種樹狀結構的每一級都是一個功能完備的個體。

配圖:https://realtimeboard.com/static/images/page/examples/detail/organizational-chart.png

官方定義

將對象組合成樹形結構以表示"部分-總體"的層次結構。組合模式使得用戶對單個對象和組合對象的使用具備一致性。

這個組合模式不是「組合優於繼承」的那種「組合」,這裏是狹義的指代一種特定場景,就是如配圖描述的一種樹狀結構。

理解這個模式要知道三個設定:

  • Component:一個有功能性的組件。
  • Leaf:它實現了組件。
  • Composite:它既實現了組件,他又包含多個組件。

protocol Component {
    func someMethod()
}

class Leaf: Component {
    func someMethod() {
        // Leaf
    }
}

class Composite: Component {
    var components = [Component]()
    func someMethod() {
        // Composite
    }
}

let leaf = Leaf()
let composite = Composite()
composite.components += [leaf]

composite.someMethod()
leaf.someMethod()
複製代碼

這個模式的精髓就是Composite這個角色,事實上Leaf能夠看作一個特殊的Compostie。因爲他便可以是一個功能執行者,又能夠包含其它節點,這個特性能夠派生出泛用度很高的樹狀結構。


Decorator 裝飾者模式

若是點咖啡時價格的計算是牛奶(糖(咖啡(價格: 19元)))就行了

配圖:http://cdn.marksdailyapple.com/wordpress/wp-content/themes/Marks-Daily-Apple-Responsive/images/blog2/coffee.jpg

官方定義

動態地給一個對象添加一些額外的職責。就增長功能來講,裝飾器模式相比生成子類更爲靈活。

下面咱們就用裝飾者模式的思想來設計這個點咖啡程序:

代碼

protocol Component {
    var cost: Int { get }
}

protocol Decorator: Component {
    var component: Component { get }
    init(_ component: Component)
}

struct Coffee: Component {
    var cost: Int
}

struct Sugar: Decorator {
    var cost: Int {
        return component.cost + 1
    }
    var component: Component
    init(_ component: Component) {
        self.component = component
    }
}

struct Milk: Decorator {
    var cost: Int {
        return component.cost + 2
    }
    var component: Component
    init(_ component: Component) {
        self.component = component
    }
}

Milk(Sugar(Coffee(cost: 19))).cost
複製代碼

當你的需求是零散的不斷給「主菜加點佐料」的時候,而且這些佐料會常常變化,那麼這個模式就能夠有效的解決排列組合產生的類爆炸

理解的一個關鍵點就是區分組件Component裝飾者Decorator兩個角色,單純組件的實現者(咖啡)是被裝飾的對象,他再也不能裝飾別人。

這個模式沒有一個集中的計算器,每個裝飾者都參與了部分計算並輸出當下的結果。


Facade 外觀模式

外觀模式就是化繁爲簡。

配圖:http://www.3dmgame.com/news/201709/3684484.html

官方定義

爲子系統中的一組接口提供一個一致的界面,外觀模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。

值得一提的是,這個單詞讀音很怪。

在談起外觀模式的時候,經常是指對一個複雜的(舊)代碼庫在不改變其內在的狀況下,包裝一層易於調用的表層API。

外觀模式不會採用繼承,而是用接口和組合。

protocol Facade {
    func simpleMethod()
}

class LegacyCode {
    func someMethod1() { }
    func someMethod2() { }
}

extension LegacyCode: Facade {
    func simpleMethod() {
        someMethod1()
        someMethod2()
    }
}

class Client {
    let f: Facade = LegacyCode()
}

let c = Client()
c.f.simpleMethod()
複製代碼

Flyweight 享元模式

享元模式就像CPU的Cache Memory,它經過對緩存的命中來實現速度的提高和內存的下降。

配圖:http://www.computerhope.com/issues/pictures/cpu_cache_die.jpg

官方定義

運用共享技術有效地支持大量細粒度的對象。

享元模式其實就是指一套緩存系統。 顯然他是一種複合模式,使用工廠模式來創造實例。 適用場景是系統中存在重複的對象建立過程。 好處是節省了內存加快了速度。

struct TargetObject {
    var title: String?
    func printTitle() {
        print(title)
    }
}

class Cache {
    var targetObjects = [String: TargetObject]()
    func lookup(key: String) -> TargetObject {
        if targetObjects.index(forKey: key) == nil {
            return TargetObject()
        }
        return targetObjects[key]!
    }
}

let c = Cache()
c.targetObjects["Test"] = TargetObject(title: "Test")
c.lookup(key: "123").printTitle() // nil
c.lookup(key: "Test").printTitle() // Test
複製代碼

Proxy 代理模式

代理模式讓一個類成爲了另外一個類的實際接口。

配圖:http://tse2.mm.bing.net/th?id=OIP.igizh16RdxH-Xq9jCR7KLgHaCx&w=300&h=112&c=7&o=5&dpr=2&pid=1.7

官方定義

爲其餘對象提供一種代理以控制對這個對象的訪問。

有兩種常見的代理場景:

  • Protection proxy: 出於安全考慮,經過一個表層的類間接調用底層的類。
  • Virtual proxy:出於性能考慮,經過一個低耗的類延遲調用一個高耗的類。

但他們的實現是相似的:

protocol Subject {
    mutating func operation()
}

struct SecretObject: Subject {
    func operation() {
        // real implementation
    }
}

struct PublicObject: Subject {
    private lazy var s = SecretObject()
    mutating func operation() {
        s.operation()
    }
}

var p = PublicObject()
p.operation()
複製代碼

SecretObject既能夠看作一個隱藏類,又能夠看作一個高費的類。經過PublicObject對他的代理,能夠實現信息隱藏和延遲加載兩個特性。


Chain of responsibility 責任鏈模式

責任鏈模式就像去取錢,每一面值都參與了計算和職責傳遞。

配圖:http://www.joezimjs.com/wp-content/uploads/chain_of_responsibility_atm.png

官方定義

避免請求發送者與接收者耦合在一塊兒,讓多個對象都有可能接收請求,將這些對象鏈接成一條鏈,而且沿着這條鏈傳遞請求,直到有對象處理它爲止。

UIKit的touch事件就應用了責任鏈模式

這個模式的實現就是既實現一個接口,又組合這個接口,這樣本身執行完畢後就能夠調用下一個執行者。

protocol ChainTouchable {
    var next: ChainTouchable? { get }
    func touch()
}

class ViewA: ChainTouchable {
    var next: ChainTouchable? = ViewB()
    func touch() {
        next?.touch()
    }
}

class ViewB: ChainTouchable {
    var next: ChainTouchable? = ViewC()
    func touch() {
        next?.touch()
    }
}

class ViewC: ChainTouchable {
    var next: ChainTouchable? = nil
    func touch() {
        print("C")
    }
}

let a = ViewA()
a.touch() // OUTPUT: C
複製代碼

Command 命令模式

命令模式就是一種指令系統。

配圖:http://cdn.wikimg.net/strategywiki/images/thumb/e/e3/Light-bot_2-3.jpg/914px-Light-bot_2-3.jpg

官方定義

將一個請求封裝成一個對象,從而使您能夠用不一樣的請求對客戶進行參數化。

命令模式有兩個特徵:

  • 命令邏輯對象化,這個命令對象能夠從外界經過參數傳入,也可內部實現。
  • 支持undo,由於每一個命令對象本身知道如何撤銷(反命令),因此能夠封裝到命令對象內部。

protocol Command {
    var operation: () -> Void { get }
    var backup: String { get }
    func undo()
}

struct ConcreteCommand: Command {
    var backup: String
    var operation: () -> Void
    func undo() {
        print(backup)
    }
}

struct Invoker {
    var command: Command
    func execute() {
        command.operation()
    }
    func undo() {
        command.undo()
    }
}

let printA = ConcreteCommand(backup: "Default A") {
    print("A")
}
let i1 = Invoker(command: printA)
i1.execute() // OUTPUT: A

let printB = ConcreteCommand(backup: "Default B") {
    print("B")
}
let i2 = Invoker(command: printB)
i2.execute() // OUTPUT: B
i2.undo() // OUTPUT: Default B
複製代碼

Interpreter 解釋器模式

有了解釋器,Skyrim的龍語也不在話下。

配圖:https://staticdelivery.nexusmods.com/mods/110/images/thumbnails/32821-3-1362476170.jpg

官方定義

給定一個語言,定義它的文法表示,並定義一個解釋器,這個解釋器使用該標識來解釋語言中的句子。

咱們能夠實現一個能計算文本「1加1」的解釋器。

protocol Expression {
    func evaluate(_ context: String) -> Int
}

struct MyAdditionExpression: Expression {
    func evaluate(_ context: String) -> Int {
        return context.components(separatedBy: "加")
            .flatMap { Int($0) }
            .reduce(0, +)
    }
}

let c = MyAdditionExpression()
c.evaluate("1加1") // OUTPUT: 2
複製代碼

Iterator 迭代器模式

迭代器就像回轉壽司,保證每一道菜品都能獲得展現。

配圖:http://www.sohu.com/a/207894434_99978064

官方定義

提供一種方法順序訪問一個聚合對象中各個元素, 而又無須暴露該對象的內部表示。

迭代器就是能用hasNext和Next來遍歷的一種集合元素。

他很像責任鏈,可是責任鏈是一個隨時可斷的鏈條(有可能在某一個節點再也不把責任下放),他不強調一次完整的遍歷。迭代器更像一次次的循環,每次循環都強調完整性,因此更適合集合的場景。

還有一個區別是迭代器是提供元素,而責任鏈在每個經手人那作業務。

protocol AbstractIterator {
    func hasNext() -> Bool
    func next() -> Int?
}

class ConcreteIterator: AbstractIterator {
    private var currentIndex = 0
    var elements = [Int]()
    

    func next() -> Int? {
        guard currentIndex < elements.count else { currentIndex = 0; return nil }
        defer { currentIndex += 1 }
        return elements[currentIndex]
    }
    
    func hasNext() -> Bool {
        guard currentIndex < elements.count else { currentIndex = 0; return false }
        return elements[currentIndex] != nil
    }
}

protocol AbstractCollection {
    func makeIterator() -> AbstractIterator
}

class ConcreteCollection: AbstractCollection {
    let iterator = ConcreteIterator()
    func add(_ e: Int) {
        iterator.elements.append(e)
    }
    func makeIterator() -> AbstractIterator {
        return iterator
    }
}

let c = ConcreteCollection()
c.add(1)
c.add(2)
c.add(3)

let iterator = c.makeIterator()
while iterator.hasNext() {
    print(iterator.next() as Any)
}
複製代碼

Mediator 中介模式

中介模式是對象間發消息的傳話筒。

配圖:http://www.quanjing.com/imgbuy/top-959496.html

官方定義

用一箇中介對象來封裝一系列的對象交互,中介者使各對象不須要顯式地相互引用,從而使其耦合鬆散,並且能夠獨立地改變它們之間的交互。

中介模式對於通訊關係複雜的系統有很好的解耦效果

它和觀察者模式很像,區別在於觀察者是不關心接受方的廣播,中介者是介入兩個(或多個)對象之間的定點消息傳遞。

protocol Receiver {
    func receive(message: String)
}

protocol Mediator: class {
    func notify(message: String)
    func addReceiver(_ receiver: Receiver)
}

class ConcreteMediator: Mediator {
    var recipients = [Receiver]()
    func notify(message: String) {
        recipients.forEach { $0.receive(message: message) }
    }
    func addReceiver(_ receiver: Receiver) {
        recipients.append(receiver)
    }
}

protocol Component: Receiver {
    var mediator: Mediator? { get }
}

struct ConcreteComponent: Component {
    weak var mediator: Mediator?
    var name: String
    func receive(message: String) {
        print(name, " receive: ", message)
    }
}

var mediator = ConcreteMediator()

let c1 = ConcreteComponent(mediator: mediator, name: "c1")
let c2 = ConcreteComponent(mediator: mediator, name: "c2")
let c3 = ConcreteComponent(mediator: mediator, name: "c3")

mediator.addReceiver(c1)
mediator.addReceiver(c2)
mediator.addReceiver(c3)

//c1 receive: hi
//c2 receive: hi
//c3 receive: hi
c1.mediator?.notify(message: "hi")
複製代碼

Memento 備忘錄模式

備忘錄就是遊戲存檔。

配圖:http://gearnuke.com/wp-content/uploads/2015/05/witcher3-ps4-savedata-2.jpg

官方定義

在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象以外保存這個狀態。

這個模式有兩個角色,一個是要存儲的類型自己(Memento)和執行存儲操做的保管人(Caretaker)。

typealias Memento = [String: String] // chatper: level
protocol MementoConvertible {
    var memento: Memento { get }
    init?(memento: Memento)
}

class GameState: MementoConvertible {
    var memento: Memento {
        return [chapter: level]
    }
    var chapter: String
    var level: String
    
    required init?(memento: Memento) {
        self.chapter = memento.keys.first ?? ""
        self.level = memento.values.first ?? ""
    }
    init(chapter: String, level: String) {
        self.chapter = chapter
        self.level = level
    }
}

protocol CaretakerConvertible {
    static func save(memonto: Memento, for key: String)
    static func loadMemonto(for key: String) -> Memento?
}

class Caretaker: CaretakerConvertible {
    static func save(memonto: Memento, for key: String) {
        let defaults = UserDefaults.standard
        defaults.set(memonto, forKey: key)
        defaults.synchronize()
    }
    
    static func loadMemonto(for key: String) -> Memento? {
        let defaults = UserDefaults.standard
        return defaults.object(forKey: key) as? Memento
    }

}

let g = GameState(chapter: "Prologue", level: "0")
// after a while
g.chapter = "Second"
g.level = "20"
// want a break
Caretaker.save(memonto: g.memento, for: "gamename")
// load game
let gameState = Caretaker.loadMemonto(for: "gamename") // ["Second": "20"]
複製代碼

Observer 觀察者模式

觀察者模式就像預購,支付了就坐等收貨,省心省力。

配圖:http://images.pushsquare.com/news/2015/01/what_the_hecks_going_on_with_playstation_store_pre-orders_in_europe/attachment/0/original.jpg

官方定義

定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,全部依賴於它的對象都獲得通知並被自動更新。

觀察者模式定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,它的全部依賴者都會收到通知並自動更新。

觀察者模式適用在一個被觀察者(數據源)要通知多個觀察者的場景。

這個模式主要經過添加一層接口,來把觀察者的具體類型擦除,從何實現鬆耦合。(針對接口編程,而非實現)

觀察者模式一個最重要的特色就是它是一種推模型(PUSH),在不少狀況下比拉模型更高效和即時。

protocol Observable {
    var observers: [Observer] { get }
    func add(observer: Observer)
    func remove(observer: Observer)
    func notifyObservers()
}

class ConcreteObservable: Observable {
    var observers = [Observer]()
    func add(observer: Observer) {
        observers.append(observer)
    }
    func remove(observer: Observer) {
        if let index = observers.index(where: { $0 === observer }) {
            observers.remove(at: index)
        }
    }
    func notifyObservers() {
        observers.forEach { $0.update() }
    }
}

protocol Observer: class {
    func update()
}

class ConcreteObserverA: Observer {
    func update() { print("A") }
}

class ConcreteObserverB: Observer {
    func update() { print("B") }
}

//////////////////////////////////

let observable = ConcreteObservable()
let a = ConcreteObserverA()
let b = ConcreteObserverB()
observable.add(observer: a)
observable.add(observer: b)
observable.notifyObservers() // output: A B

observable.remove(observer: b)
observable.notifyObservers() // output: A
複製代碼

State 狀態模式

狀態模式就像馬里奧吃了各類蘑菇後的變身。

配圖:http://www.superluigibros.com/images/super_mario_bros_3_power_ups.jpg

官方定義

容許對象在內部狀態發生改變時改變它的行爲,對象看起來好像修改了它的類。

狀態模式很像策略模式,可是他封裝的不是算法,而是一個一個本體下的不一樣狀態。

protocol State {
    func operation()
}

class ConcreteStateA: State {
    func operation() {
        print("A")
    }
}

class ConcreteStateB: State {
    func operation() {
        print("B")
    }
}

class Context {
    var state: State = ConcreteStateA()
    func someMethod() {
        state.operation()
    }
}

let c = Context()
c.someMethod() // OUTPUT: A
c.state = ConcreteStateB() // switch state
c.someMethod() // OUTPUT: B
複製代碼

Strategy 策略模式

策略模式就像不一樣的交通路線,能夠相互替換,都能達到終點。

配圖:高德地圖iOS

官方定義

定義一系列的算法,把它們一個個封裝起來, 而且使它們可相互替換。

這個模式主要仍是在解決一個穩定的繼承樹當遇到需求變化時的狼狽。他把變化的幾種狀況分門別類的封裝好,而後把本身變化的部分隔離成一個接口實例(interface/protocol),讓子類繼承後來作選擇題。

一個須要轉變的思惟就是:建模一個類的時候不能只侷限於物體(object),也能夠是行爲(behavior)的封裝。

用繼承來替換interface實現多態的behavior也是理論可行的。 這個模式還有一個強大之處:他是動態的。就是至於子類選擇了什麼行爲,不見得是寫代碼的時候寫死的;也能夠是相似用戶點擊一下切換了算法。

protocol WeaponBehavior {
    func use()
}

class SwordBehavior: WeaponBehavior {
    func use() { print("sword") }
}

class BowBehavior: WeaponBehavior {
    func use() { print("bow") }
}

class Character {
    var weapon: WeaponBehavior?
    func attack() {  weapon?.use() }
}

class Knight: Character {
    override init() {
        super.init()
        weapon = SwordBehavior()
    }
}

class Archer: Character {
    override init() {
        super.init()
        weapon = BowBehavior()
    }
}

///////////////////////////////////

Knight().attack() // output: sword
Archer().attack() // output: bow
複製代碼

Template Method 模板方法模式

模板方法就是抽象類衍生出來的繼承樹。

配圖:https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=3167143621,3882552175&fm=27&gp=0.jpg

官方定義

定義一個操做中的算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類能夠不改變一個算法的結構便可重定義該算法的某些特定步驟。

模板方法指的就是存在在抽象類中的方法聲明,由子類具體實現。這樣這套方法產生了一套模具不一樣產品的派生效果。

經過抽象類和子類實現多態。

很惋惜Swift/Objective-C都沒有抽象類, 因此用通常的class來實現

class Soldier {
    func attack() {} // <-- Template Method
    private init() {} // <-- avoid creation
}

class Paladin: Soldier {
    override func attack() {
        print("hammer")
    }
}

class Archer: Soldier {
    override func attack() {
        print("bow")
    }
}
複製代碼

Visitor 訪問者模式

訪問者模式就像一個個酒店,他們都有接待你的接口,你入住後又會利用它的基礎設施。

配圖:https://marketingland.com/wp-content/ml-loads/2014/08/hotel-bell-customer-service-ss-1920.jpg

官方定義

主要將數據結構與數據操做分離。

號稱最複雜的設計模式,先看一下實現代碼。

protocol Visitor {
    func visit(_ c: ComponentA)
    func visit(_ c: ComponentB)
}

struct ConcreteVisitor: Visitor {
    func visit(_ c: ComponentA) {
        c.featureA()
    }
    func visit(_ c: ComponentB) {
        c.featureB()
    }
}

protocol Component {
    func accept(_ v: Visitor)
}

struct ComponentA: Component {
    func featureA() {
        print("Feature A")
    }
    func accept(_ v: Visitor) {
        v.visit(self)
    }
}

struct ComponentB: Component {
    func featureB() {
        print("Feature B")
    }
    func accept(_ v: Visitor) {
        v.visit(self)
    }
}

let components: [Component] = [ComponentA(), ComponentB()]
components.forEach {
    $0.accept(ConcreteVisitor())
}
複製代碼

要理解這個模式須要瞭解一些概念:

經過這篇文章能夠了解到, 其實設計模式(GoF)中的Visitor模式就是Java解決Double Dispatch的一種應用。

咱們用Swift實現一下文中雙重派發問題

protocol Event {}
class BlueEvent: Event {}
class RedEvent: Event {}

class Handler {
    func handle(_ e: Event) {
        print("Event")
    }
    func handle(_ r: RedEvent) {
        print("RedEvent")
    }
    func handle(_ b: BlueEvent) {
        print("BlueEvent")
    }
}

let b = BlueEvent()
let r = RedEvent()
let c = Handler()

c.handle(b) // OUTPUT: BlueEvent
c.handle(r) // OUTPUT: RedEvent
複製代碼

驗證發現,Swift是支持雙重派發的。

那什麼是雙重派發(Double Dispatch)

要解釋這個,要先搞清楚什麼是派發(Dispatch)

派發或者說Single Dispatch,Dynamic Dispatch,wiki是這樣解釋的:

In most object-oriented systems, the concrete function that is called from a function call in the code depends on the dynamic type of a single object and therefore they are known as single dispatch calls, or simply virtual function calls. 抓要點說,就是多態的狀況下,在運行時,虛方法是由哪一個具體實現執行了,這樣的綁定動做就是派發。

那什麼虛方法? 看一下wiki的解釋:

In short, a virtual function defines a target function to be executed, but the target might not be known at compile time. 虛方法就是編譯期還沒肯定實現的方法,咱們能夠理解成接口中聲明的方法。

好,咱們如今知道了一次虛方法的綁定就是派發,雙重派發就是兩次綁定操做。

那雙重派發的代碼是如何觸發派發的呢?

  • overload 重載 重載產生的多態引發一次派發
  • override 重寫 接口實現的多態引發第二次派發

理解這些就知道這個模式在折騰什麼了。

固然他也有另外的好處,就是可讓具體的業務類能夠丟到集合裏批量的執行accept visitor。

相關文章
相關標籤/搜索