pattern模式 swift_设计模式 pattern 书 - CSDN
  • Swift 模式(Patterns)

    2016-06-27 21:49:14
    模式pattern)代表了单个值或者复合值的结构。例如,元组(1, 2)的结构是逗号分隔的,包含两个元素的列表。因为模式代表一种值的结构,而不是特定的某个值,你可以把模式和各种同类型的值匹配起来。比如,(x, y)...

    模式(pattern)代表了单个值或者复合值的结构。例如,元组(1, 2)的结构是逗号分隔的,包含两个元素的列表。因为模式代表一种值的结构,而不是特定的某个值,你可以把模式和各种同类型的值匹配起来。比如,(x, y)可以匹配元组(1, 2),以及任何含两个元素的元组。除了将模式与一个值匹配外,你可以从复合值中提取出部分或全部,然后分别把各个部分和一个常量或变量绑定起来。

    swift语言中模式有2个基本的分类:一类能成功和任何值的类型相匹配,另一类在运行时(runtime)和某特定值匹配时可能会失败。

    第一类模式用于解构简单变量,常量和可选绑定中的值。此类模式包括通配符模式(wildcard patterns),标识符模式(identifier patterns),以及任何包含了它们的值绑定模式(value binding patterns)或者元祖模式(tuple patterns)。你可以为这类模式指定一个类型标注(type annotation)从而限制它们只能匹配某种特定类型的值。

    第二类模式用于全模式匹配,这种情况下你用来相比较的值在运行时可能还不存在。此类模式包括枚举用例模式(enumeration case patterns),可选模式(optional patterns),表达式模式(expression patterns)和类型转换模式(type-casting patterns)。你在switch语句的case标签中,do语句的catch从句中,或者在if, while, guardfor-in语句的case条件句中使用这类模式。

    模式(Patterns) 语法
    模式 → 通配符模式 类型标注 可选
    模式 → 标识符模式 类型标注on) 可选
    模式 → 值绑定模式
    模式 → 元组模式 类型标注 可选
    模式 → 枚举用例模式
    模式 → 可选模式
    模式 → 类型转换模式
    模式 → 表达式模式

    通配符模式(Wildcard Pattern)

    通配符模式由一个下划线(_)构成,且匹配并忽略任何值。当你不在乎被匹配的值时可以使用该模式。例如,下面这段代码在闭区间1...3中循环,每次循环时忽略该区间内的当前值:

    for _ in 1...3 {
        // Do something three times.
    }
    

    通配符模式语法
    通配符模式 → _

    标识符模式(Identifier Pattern)

    标识符模式匹配任何值,并将匹配的值和一个变量或常量绑定起来。例如,在下面的常量声明中,someValue是一个标识符模式,匹配了类型是Int42

    let someValue = 42
    

    当匹配成功时,42被绑定(赋值)给常量someValue

    如果一个变量或常量声明的左边的模式是一个标识符模式,那么这个标识符模式是一个隐式的值绑定模式(value-binding pattern)。

    标识符模式语法
    标识符模式 → 标识符

    值绑定模式(Value-Binding Pattern)

    值绑定模式把匹配到的值绑定给一个变量或常量名。把绑定匹配到的值绑定给常量时,用关键字let,绑定给变量时,用关键字var

    在值绑定模式中的标识符模式会把新命名的变量或常量与匹配值做绑定。例如,你可以拆开一个元组的元素,然后把每个元素绑定到其相应一个的标识符模式中。

    let point = (3, 2)
    switch point {
        // Bind x and y to the elements of point.
    case let (x, y):
        print("The point is at (\(x), \(y)).")
    }
    // prints "The point is at (3, 2).”
    

    在上面这个例子中,let将元组模式(x, y)分配到各个标识符模式。正是由于这么做,switch语句中case let (x, y):case (let x, let y):匹配到的值是一样的。

    值绑定(Value Binding)模式语法
    值绑定模式 → var 模式 | let 模式

    元组模式(Tuple Pattern)

    元组模式是逗号分隔的,有零个或多个模式的列表,并被一对圆括号括起来。元组模式匹配相应元组类型的值。

    你可以使用类型标注去限制一个元组模式能匹配哪些种元组类型。例如,在常量声明let (x, y): (Int, Int) = (1, 2)中的元组模式(x, y): (Int, Int)只匹配两个元素都是Int这种类型的元组。如果仅需要限制一个元组模式中的某几个元素,只需要直接对这几个元素提供类型标注即可。例如,在let (x: String, y)中的元组模式可以和任何有两个元素,且第一个元素类型是String的元组类型匹配。

    当元组模式被用在for-in语句或者变量或常量声明时,它仅可以包含通配符模式,标识符模式,可选模式或者其他包含这些模式的元祖模式。比如下面这段代码就不正确,因为(x, 0)中的元素0是一个表达式模式:

    let points = [(0, 0), (1, 0), (1, 1), (2, 0), (2, 1)]
    // This code isn't valid.
    for (x, 0) in points {
        /* ... */
    }
    

    对于只包含一个元素的元组,括号是不起作用的。模式只匹配这个单个元素的类型。举例来说,下面3条语句是等效的:

    let a = 2        // a: Int = 2
    let (a) = 2      // a: Int = 2
    let (a): Int = 2 // a: Int = 2
    

    元组模式语法
    元组模式 → ( 元组模式元素列表 可选 )
    元组模式元素列表 → 元组模式元素 | 元组模式元素 , 元组模式元素列表
    元组模式元素 → 模式

    枚举用例模式(Enumeration Case Pattern)

    一个枚举用例模式匹配现有的某个枚举类型的某个用例(case)。枚举用例模式出现在switch语句中的case标签中,以及ifwhileguardfor-in语句的case条件中。

    如果你准备匹配的枚举用例有任何关联的值,则相应的枚举用例模式必须指定一个包含每个关联值元素的元组模式。关于使用switch语句来匹配包含关联值枚举用例的例子,请参阅Associated Values.

    枚举用例模式语法
    enum-case-pattern → 类型标识 可选 . 枚举的case名 元组模式 可选

    可选模式(Optional Pattern)

    可选模式与封装在一个Optional(T)或者一个ExplicitlyUnwrappedOptional(T)枚举中的Some(T)用例相匹配。可选模式由一个标识符模式和紧随其后的一个问号组成,在某些情况下表现为枚举用例模式。

    由于可选模式是optionalImplicitlyUnwrappedOptional枚举用例模式的语法糖(syntactic sugar),下面的2种写法是一样的:

    let someOptional: Int? = 42
    // Match using an enumeration case pattern
    if case .Some(let x) = someOptional {
        print(x)
    }
    
    // Match using an optional pattern
    if case let x? = someOptional {
        print(x)
    }
    

    如果一个数组的元素是可选类型,可选模式为for-in语句提供了一种在该数组中迭代的简便方式,只为数组中的非空non-nil元素执行循环体。

    let arrayOfOptionalInts: [Int?] = [nil, 2, 3, nil, 5]
    // Match only non-nil values
    for case let number? in arrayOfOptinalInts {
        print("Found a \(number)")
    }
    //Found a 2
    //Found a 3
    //Found a 5
    

    可选模式语法
    可选模式 → 标识符模式 ?

    类型转换模式(Type-Casting Patterns)

    有两种类型转换模式,is模式和as模式。这两种模式只出现在switch语句中的case标签中。is模式和as模式有以下形式:

    is type
    pattern as type

    is模式仅当一个值的类型在运行时(runtime)和is模式右边的指定类型一致 - 或者是该类型的子类 - 的情况下,才会匹配这个值。is模式和is操作符有相似表现,它们都进行类型转换,却舍弃返回的类型。

    as模式仅当一个值的类型在运行时(runtime)和as模式右边的指定类型一致 - 或者是该类型的子类 - 的情况下,才会匹配这个值。如果匹配成功,被匹配的值的类型被转换成as模式左边指定的模式。

    关于使用switch语句来匹配is模式和as模式值的例子,请参阅Type Casting for Any and AnyObject

    类型转换模式语法
    type-casting-pattern → is模式 | as模式
    is模式 → is 类型
    as模式 → 模式 as 类型

    表达式模式(Expression Pattern)

    一个表达式模式代表了一个表达式的值。表达式模式只出现在switch语句中的case标签中。

    由表达式模式所代表的表达式与使用了Swift标准库中~=操作符的输入表达式的值进行比较。如果~=操作符返回true,则匹配成功。默认情况下,~=操作符使用==操作符来比较两个相同类型的值。它也可以将一个整型数值与一个Range对象中的一段整数区间做匹配,正如下面这个例子所示:

    let point = (1, 2)
    switch point {
    case (0, 0):
        print("(0, 0) is at the origin.")
    case (-2...2, -2...2):
        print("(\(point.0), \(point.1)) is near the origin.")
    default:
        print("The point is at (\(point.0), \(point.1)).")
    }
    // prints "(1, 2) is near the origin.”
    

    你可以重载~=操作符来提供自定义的表达式匹配行为。比如你可以重写上面的例子,拿point表达式去比较字符串形式的点。

    // Overload the ~= operator to match a string with an integer
    func ~=(pattern: String, value: Int) -> Bool {
        return pattern == "\(value)"
    }
    switch point {
    case ("0", "0"):
        print("(0, 0) is at the origin.")
    default:
        print("The point is at (\(point.0), \(point.1)).")
    }
    // prints "(1, 2) is near the origin.”
    

    表达式模式语法
    表达式模式 → 表达式

    展开全文
  • 什么是模式? ...Swift中的模式有: 通配符模式(Wildcard Pattern) 标识符模式(Identifier Pattern) 值绑定模式(Value-Binding Pattern) 元组模式(Tuple Pattern) 枚举Case模式(Enu...
    • 什么是模式?

    • 模式是用于匹配的规则,比如switch的case、捕捉错误的catch、if\guard\while\for语句的条件等

    • Swift中的模式有:

    1. 通配符模式(Wildcard Pattern)
    2. 标识符模式(Identifier Pattern)
    3. 值绑定模式(Value-Binding Pattern)
    4. 元组模式(Tuple Pattern)
    5. 枚举Case模式(Enumeration Case Pattern)
    6. 可选模式(Optional Pattern)
    7. 类型转换模式(Type-Casting Pattern)
    8. 表达式模式(Expression Pattern)

    通配符模式(Wildcard Pattern)

    • _ 匹配任何值
    • _? 匹配非nil值
    enum Life {
        case human(name: String, age: Int?)
        case animal(name: String, age: Int?)
    }
    
    func check(_ life: Life) {
        switch life {
            case .human(let name, _):
                print("human", name)
            case .animal(let name, _?):
                print("animal", name)
            default:
                print("other")
        }
    }
    check(.human(name: "Rose", age: 20)) // human Rose
    check(.human(name: "Jack", age: nil)) // human Jack
    check(.animal(name: "Dog", age: 5)) // animal Dog
    check(.animal(name: "Cat", age: nil)) // other
    
    var num: Int? = 10
    switch num {
        case let v?:
            print(v)
        case nil: 
            print("nil")
    }
    

    标识符模式( Identifier Pattern)

    • 给对应的变量、常量名赋值
    var age = 10
    let name = "Jack"
    

    值绑定模式( Value-Binding Pattern)

    let point = (3, 2)
    switch point {
        case let (x, y):
            print("The point is at (\(x),\(y)).")
    }
    

    元组模式( Tuple Pattern)

    let points = [(0, 0), (1, 0), (2, 0)]
    for (x, _) in points {
        print(x)
    }
    
    let name: String? = "jack"
    let age = 18
    let info: Any = [1, 2]
    switch (name, age, info) {
        case (_?, _, _ as String):
            print("case")
        default:
            print("default")
    } // default
    
    var scores = ["jack": 98, "rose": 100, "kate":86]
    for (name, score) in scores {
        print(name, score)
    }
    

    枚举case模式( Enumeration Case Pattern)

    • if case语句等价于只有1个case的switch语句
    let age = 2
    // 原来的写法
    if age >= 0 && age <= 9 {
        print("[0, 9]")
    }
    // 枚举Case模式
    if case 0...9 = age {
        print("[0, 9]")
    }
    guard case 0...9 = age else { return }
    print("[0, 9]")
    
    switch age {
        case 0...9: print("[0, 9]")
        default: break
    }
    
    let ages: [Int?] = [2, 3, nil, 5]
    for case nil in ages {
        print("有nil值")
        break
    } // 有nil值
    
    

    可选模式(Optional Pattern)

    let age: Int? = 42
    if case .some(let x) = age { print(x) }
    if case let x? = age { print(x) }
    
    let ages: [Int?] = [nil, 2, 3, nil, 5]
    for case let age? in ages {
        print(age)
    } // 2 3 5
    
    let ages: [Int?] = [nil, 2, 4, 5]
    for item in ages {
        if let age = item {
            print(age)
        }
    } // 跟上面的for,效果等价的
    
    func check(_ num: Int?) {
        switch num {
            case 2?: print("2")
            case 4?: print("4")
            case 6?: print("6")
            case _?: print("other")
            case _: print("nil")
        }
    }
    check(4) // 4
    check(8) // other
    check(nil) // nil
    

    类型转换模式(Type-Casting Pattern)

    let num: Any = 6
    switch num {
        case is Int:
            //编译器依然认为num是Any类型
            print("is Int", num)
    //case let n as Int:
    //    print("as Int", n + 1)
        default:
            break
    }
    
    class Animal { 
        func eat() { 
            print(type(of: self), "eat")
        }
    } 
    class Dog: Animal { 
        func run() { 
            print(type(of: self), "run")
        }
    } 
    class Cat: Animal { 
        func jump() { 
            print(type(of: self), "jump")
        }
    } 
    func check(_ animal: Animal) {
        switch animal {
            case let dog as Dog:
                dog.eat()
                dog.run()
            case is Cat:
                animal.eat()
            default: break    
        }
    }
    // Dog eat
    // Dog run
    check(Dog())
    // Cat eat
    //check(Cat())
    

    表达式模式(Expression Pattern)

    • 表达式模式用在case中
    let point = (1, 2)
    switch point {
        case (0, 0)
            print("(0, 0) is at the origin.")
        case (-2...2, -2...2):
            print("(\(point.0), \(point.1)) isnear the origin.")
        default:
            print("The point is at (\(point.0),\(point.1)).")
    } // (1, 2) is near the origin
    

    自定义模式(Expression Pattern)

    • 可以通过重载运算符,自定义表达式模式的匹配规则
    struct Student {
        var score = 0, name = ""
        static func ~= (pattern: Int, value: Student) -> Bool { value.score >= pattern }
        static func ~= (pattern: ClosedRange<Int>, value: Student) -> Bool { pattern.contains(value.score) }
        static func ~= (pattern: Range<Int>, value: Student) -> Bool { pattern.contains(value.score) }
    }
    
    var stu = Student(score: 75, name: "Jack")
    switch stu {
        case 100: print(">= 100")
        case 90: print(">= 90")
        case 80..<90: print("[80, 90)")
        case 60...79: print("[60, 79]")
        case 0: print(">= 0")
        default: break
    } // [60, 79]
    
    if case 60 = stu {
        print(">= 60")
    }
    
    var info = (Student(score: 70, name: "Jack"), "及格")
    switch info {
        case let (60, text): print(text)
        default: break
    } // 及格
    

    extension String {
        static func ~= (pattern: (String) -> Bool, value: String) -> Bool {
            pattern(value)
        }
    }
    func hasPrefix(_ s:String) -> ((String) -> Bool) { { $0.hasPrefix(s) } }
    func hasSuffix(_ s:String) -> ((String) -> Bool) { { $0.hasSuffix(s) } }
    var str = "jack"
    switch str {
        case hasPrefix("j"), hasSuffix("k"):
            print("以j开头,或者以k结尾")
        default: break
    } // 以j开头,或者以k结尾
    

    func isEven(_ i: Int) -> Bool { i % 2 == 0 }
    func isOdd(_ i: Int) -> Bool { i % 2 != 0 }
    extension Int {
        static func ~= (pattern: (Int) -> Bool, value: Int) -> Bool {
            pattern(value)
        }
    }
    var age = 9
    switch age {
        case isEven:
            print("偶数")
        case isOdd:
            print("奇数")
        default:
            print("其他")
    }
    
    prefix operator ~>
    prefix operator ~>=
    prefix operator ~<
    prefix operator ~<=
    prefix func ~> (_ i: Int) -> ((Int) -> Bool) { { $0 > i } }
    prefix func ~>= (_ i: Int) -> ((Int) -> Bool) { { $0 >= i } }
    prefix func ~< (_ i: Int) -> ((Int) -> Bool) { { $0 < i } }
    prefix func ~<= (_ i: Int) -> ((Int) -> Bool) { { $0 <= i } }
    var age = 0
    switch age {
        case ~>=0:
            print("1")
        case ~>10:, ~<20
            print("2")
        default: break
    } // [0, 10]
    

    where

    • 可以使用where为模式匹配增加匹配条件
    var data = (10, "Jack")
    switch data {
        case let (age, _) where age > 10:
            print(data.1, "age > 10")
        case let (age, _) where age > 0:
            print(data.1, "age > 0")
        default: break
    }
    
    var ages = [10, 20, 44, 23, 55]
    for age in ages where age > 30 {
        print(age)
    } // 44 55
    
    protocol Stackable { associatedtype Element }
    protocol Container {
        associatedtype Stack: Stackble where Stack.Element: Equtable
    }
    
    func equal<S1: Stackable, S2: Stackable>(_ s1: S1, _ s2: S2) -> Bool 
        where S1.Element == S2.Element, S1.Element: Hashable {
            return false
    }
    
    extension Container where Self.Stack.Element: Hashable { }
    
    展开全文
  • 没有人买车会只买一个轮胎或者方向盘,大家买的都是一...生成器模式(英:Builder Pattern)是一种设计模式,又名:建造模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的
    没有人买车会只买一个轮胎或者方向盘,大家买的都是一辆包含轮胎、方向盘和发动机等多个部件的完整汽车。如何将这些部件组装成一辆完整的汽车并返回给用户,这是生成器模式需要解决的问题。下面就一起来看看生成器模式

    生成器模式

    生成器模式(Builder Pattern)也叫:建造模式,是一种对象构建模式。
    1、它可以将复杂对象的建造过与它的表现分离,使得同样的构建过程可以创建不同的表示。 
    2、Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们.用户不知道内部的具体构建细节.

    参与者



    Builder:抽象建造者,为创建一个Product对象的各个部件指定抽象接口。在该接口中一般声明两类方法,一类方法是buildPartX(),它们用于创建复杂对象的各个部件;另一类方法是getResult(),它们用于返回复杂对象。Builder既可以是抽象类,也可以是接口,iOS中使用协议。

    ConcreteBuilder:具体建造者,实现Builder的接口以构造和装配该产品的各个部件;即实现抽象builder类的所有方法,并返回一个创建好的对象。

    Director:指导者,也被称导向者,构造一个使用Builder接口的对象它负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,可以在其construct()建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造。客户端一般只需要与指挥者进行交互,在客户端确定具体建造者的类型,并实例化具体建造者对象(也可以通过配置文件和反射机制),然后通过指挥者类的构造函数或者Setter方法将该对象传入指挥者类中。

    Product:产品,表示被构造的复杂对象,包含多个部件ConcreateBuilder创建该产品的内部表示并定义它的装配过程;包含定义组成部件的类,包括将这些部件装配成最终产品的接口。

    Swift-Demo实例

    简单实现生成器模式

    //被构建的产品对象的接口
    class Product {
        var parts: String = ""
    }
    //生成器接口,定义创建一个产品对象所需的各个部件的操作
    //Builder模式是把复杂对象的创建和部件的创建分别开来
    protocol Builder {
        //创建部件
        func buildPart()
        //返回最后组装成品结果 (返回最后装配好的汽车)
        func getProduct() -> Product
    }
    //指导者,指导使用生成器的接口来构建产品对象
    class Director {
        let concreteBuilder: Builder
        init(concreteBuilder: Builder) {
            self.concreteBuilder = concreteBuilder
        }
        //将部件part最后组成复杂对象
        func construct() ->Product{
            concreteBuilder.buildPart()
            return concreteBuilder.getProduct()
        }
    }
    
    //Builder的具体实现ConcreteBuilder,通过具体完成接口Builder来构建或装配产品的部件;
    //定义并明确它所要创建的是什么具体东西;提供一个可以重新获取产品的接口:
    class ConcreteBuilder: Builder{
        
        var product = Product()
        //这里是具体如何构建partA的代码
        func buildPart() {
            product.parts = "adding part succss!"
        }
        //返回最后组装成品结果
        func getProduct() -> Product{
            return product
        }
    }
    使用如下:

    let director = Director(concreteBuilder: ConcreteBuilder())
    let product = director.construct()
    print(product.parts)//adding part succss!

    盖房子,从该博客翻译为Swift语言实现. 

    通俗讲解:Builder模式的理解,简单地说,就好象我要一盖房子住,可是我不知道怎么盖(简单的砌墙,层次较低),也不知道怎么样设计(建几个房间,几个门好看,层次较高),于是我需要找一帮民工,他们会砌墙,还得找个设计师,他知道怎么设计,我还要确保民工听设计师的领导,而设计师本身也不干活,光是下命令,这里砌一堵墙,这里砌一扇门,这样民工开始建设,最后,我可以向民工要房子了。在这个过程中,设计师是什么也没有,除了他在脑子里的设计和命令,所以要房子也是跟民工要,记住了!


    //最终产品为房子
    class Room {
        var window: String  //窗户
        var floor: String  //地板
        
        init(window: String, floor: String) {
            self.window = window
            self.floor = floor
        }
    }
    //创建者
    protocol Builder{
        //制作窗户
        func makeWindow()
        //制作地板
        func makeFloor()
        //房子建成
        func getRoom() ->Room?
    }
    
    //设计师其实就是指导者
    class Designer{
        func commandOrder(builder: Builder){
            builder.makeWindow()
            builder.makeFloor()
        }
    }
    
    //具体创建者
    class MinGong: Builder {
        var window: String?  //窗户
        var floor: String?  //地板
        
        func makeWindow() {
            window = "window"
        }
        func makeFloor() {
            floor = "floor"
        }
        func getRoom() -> Room?{
            if window == "window" && floor == "floor" {
                let room = Room(window: window!, floor: floor!)
                return room
            }
            return nil
        }
    }
    使用如下:

      //找到民工
      let minGong = MinGong()
      //找到设计师
      let designer = Designer()
      //民工遵守设计师的命令制作房子
      designer.commandOrder(builder: minGong)
      //房子建成
      if  let room = minGong.getRoom(){
          print("window: \(room.window), floor: \(room.floor)")
          //window: window, floor: floor
      }

    KFC吃汉堡包

    声明汉堡包

    // MARK: - Product
    public struct Hamburger {
        public let meat: Meat
        public let sauce: Sauces
        public let toppings: Toppings
    }
    
    extension Hamburger: CustomStringConvertible {
        public var description: String {
            return meat.rawValue + " burger"
        }
    }
    相关配菜

    // 肉类
    public enum Meat: String {
        case beef
        case chicken
        case kitten
        case tofu
    }
    
    // 酱汁
    public struct Sauces: OptionSet {
        public static let mayonnaise = Sauces(rawValue: 1 << 0)
        public static let mustard = Sauces(rawValue: 1 << 1)
        public static let ketchup = Sauces(rawValue: 1 << 2)
        public static let secret = Sauces(rawValue: 1 << 3)
    
        public let rawValue: Int
        public init(rawValue: Int) {
            self.rawValue = rawValue
        }
    }
    
    // 浇头
    public struct Toppings: OptionSet {
        public static let cheese = Toppings(rawValue: 1 << 0)
        public static let lettuce = Toppings(rawValue: 1 << 1)
        public static let pickles = Toppings(rawValue: 1 << 2)
        public static let tomatoes = Toppings(rawValue: 1 << 3)
    
        public let rawValue: Int
        public init(rawValue: Int) {
            self.rawValue = rawValue
        }
    }
    
    制作汉堡包

    // MARK: - Builder
    public class HambugerBuilder {
    
        public private(set) var meat: Meat = .beef
        public private(set) var sauces: Sauces = []
        public private(set) var toppings: Toppings = []
    
        private var soldOutMeats: [Meat] = [.kitten]
    
        public func addSauces(_ sauce: Sauces) {
            sauces.insert(sauce)
        }
    
        public func removeSauces(_ sauce: Sauces) {
            sauces.remove(sauce)
        }
    
        public func addToppings(_ topping: Toppings) {
            toppings.insert(topping)
        }
    
        public func removeToppgins(_ topping: Toppings) {
            toppings.remove(topping)
        }
    
        public func setMeat(_ meat: Meat) throws {
            guard isAvailable(meat) else {
                throw Error.soldOut
            }
            self.meat = meat
        }
    
        public func isAvailable(_ meat: Meat) -> Bool {
            return !soldOutMeats.contains(meat)
        }
    
        // 创建方式
        public func build() -> Hamburger {
            return Hamburger(meat: meat, sauce: sauces, toppings: toppings)
        }
    
        public enum Error: Swift.Error {
            case soldOut
        }
    }
    点单

    // MARK: - Director
    // 由员工来点单需要什么类型的汉堡包
    public class Employee {
        public func createCombo1() throws -> Hamburger {
            let builder = HambugerBuilder()
            try builder.setMeat(.beef)
            builder.addSauces(.secret)
            builder.addToppings([.lettuce, .tomatoes, .pickles])
            return builder.build()
        }
    
        public func createKittenSpecial() throws -> Hamburger {
            let builder = HambugerBuilder()
            try builder.setMeat(.kitten)
            builder.addSauces(.mustard)
            builder.addToppings([.lettuce, .tomatoes])
            return builder.build()
        }
    }
    使用

    // MARK: - Example
    let burgerFipper = Employee()
    
    if let combo1 = try? burgerFipper.createCombo1() {
        print(combo1.description)
    }
    
    if let kittenBurger = try? burgerFipper.createKittenSpecial() {
        print(kittenBurger.description)
    } else {
        print("Sorry, no kitten burgers here...")
    }
    iOS中的使用场景

    1、使用builder创建对象

    protocol ThemeProtocol {
        var backgroundColor: UIColor? { get }
        var textColor: UIColor? { get }
    }
    
    class Theme: ThemeProtocol {
        var backgroundColor: UIColor?
        var textColor: UIColor?
        typealias buildThemeClosure = (Theme) -> Void
    
        init(build: buildThemeClosure) {
            build(self)
        }
    }
    
    let darkTheme = Theme(build: {
        $0.backgroundColor = UIColor.black
        $0.textColor = UIColor.white
    })
    let lightTheme = Theme(build: {
        $0.backgroundColor = UIColor.white
        $0.textColor = UIColor.black
    })

    什么时候使用

    1:需要创建涉及各种部件的复杂对象,即需要一系列创建的步骤
    2:当构造过程需要以不同的方式构建对象

    3:相同的方法,不同的执行顺序,产生不同的事件结果


    模式优点

    1:松散耦合

    生成器模式可以用同一个构建算法构建出表现上完全不同的产品,实现产品构建和产品表现上的分离。生成器模式正是把产品构建的过程独立出来,使它和具体产品的表现分松散耦合,从而使得构建算法可以复用,而具体产品表现也可以很灵活地、方便地展和切换。

    2:可以很容易的改变产品的内部表示

    在生成器模式中,由于Builder对象只是提供接口给Director使用,那么具体部件创建和装配方式是被Builder接口隐藏了的,Director并不知道这些具体的实现细节。这样一来,要想改变产品的内部表示,只需要切换Builder接口的具体实现即可,不用管Director,因此变得很容易。

    3:更好的复用性

    生成器模式很好的实现构建算法和具体产品实现的分离。这样一来,使得构建产品的算法可以复用。同样的道理,具体产品的实现也可以复用,同一个产品的实现,可以配合不同的构建算法使用。

    模式缺点

    1:建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,例如很多组成部分都不相同,不适合使用建造者模式,因此其使用范围受到一定的限制。

    2:如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,增加系统的理解难度和运行成本。


    相关模式对比

    抽象工厂模式与生成器相似,因为它也可以创建复杂对象。主要的区别是生成器模式着重于一步步构造一个复杂对象。而抽象工厂模式着重于多个系列的产品对象(简单的或是复杂的)。生成器在最后的一步返回产品,而对于抽象工厂来说,产品是立即返回的.


    参考:




    展开全文
  • 观察者模式 定义:观察者设计模式定义了对象间的一种一对多的依赖关系,以便一个对象的状态发生变化时,所有依赖于它的对象都得到通知并且自动刷新。 设计原则:为了交互对象之间的松耦合设计而努力。 举例说明:...

    观察者模式

    定义:观察者设计模式定义了对象间的一种一对多的依赖关系,以便一个对象的状态发生变化时,所有依赖于它的对象都得到通知并且自动刷新。
    设计原则:为了交互对象之间的松耦合设计而努力。

    举例说明:

    比如老板在一个办公室里开会,办公室里有部分员工,在办公室的员工就是Observer(观察者),正在开会的老板就是Subject(主题:负责发送通知—Post Notification)。如果其他员工也想成为Observer,那么必须得进入(addObserver)正在开会的会议室成为观察者。员工成功观察者后收到通知得做一些事情吧(doSomething),比如记个笔记神马的。如果此时员工闹情绪,不想听老板开会了,于是通过removeObserver走出了会议室。上面这个过程其实就是观察者模式。

    一、通过示例认识观察者模式

    场景:使用boss发送通知,员工接受通知

    boss和员工显然是一对多的关系,我们将要实现的“类图”如下所示:
    在这里插入图片描述

    1、图中的SubjectType是 通知者的基类,负责发通知

    参数 说明
    info 表示发布的信息
    observerArray 保存多个观察者的数组,使用数组的原因是由于 Subject:Observers是一对多的关系
    函数 说明
    registerObserver(observer) 注册观察者
    removeObserver(observer) 移除观察者
    notigyObserver() 通知观察者

    图中Boss是SubjectType的子类,继承了父类的所有的所有属性,并重写了父类的三个方法

    函数 说明
    setInfo() 负责更新Info信息的时候调用发出通知的方法

    2、图中ObserverType 是 观察者的基类

    参数 说明
    info 存储接收到的通知信息
    函数 说明
    update() 接收到通知后要执行的方法,即boss一发通知,员工就会执行该方法
    display() 对info字段的信息进行输出

    注:把SubjectType 和 ObserverType设计成基类,不利于后期的扩展,或者会在扩展中会产生重复的代码,可以使用接口或者结合其他设计模式,能很好的解决上面的问题。

    3、类图的具体实现

    上面的Boss负责发通知,Coder 和PM负责监听 Boss发的通知

    1)通知者和观察者基类的实现

    在SubjectType基类中的observerArray中存储的是ObserverType类型(包括其子类)对象, 即 所有的观察者

    //基类
    class ObserverType{
        
        //发布的消息
        var info : String = ""
        func update(_ info : String){}
        func display(){}
    }
    class SubjectType{
        var observersArray : [ObserverType] = [ObserverType]()
        var info : String = ""
        //注册观察者
        func registerObserver(_ observer : ObserverType){}
        //移除观察者
        func removeObserver(_ observer : ObserverType){}
        //通知观察者
        func notifyObserver(){}
    }
    

    2)负责发送通知的Boss类的实现

    Boss 继承自SubjectType 。
    当Boss 执行 setInfo() 方法时,就会调用 notifyObservers()进行通知的发送。
    在Boss中的registerObserver()方法用来添加监听者(为了防止重复添加,我们在添加前先进行移除),removeObserver() 则是复杂移除监听者,notifyObservers() 是发送通知并调用观察者相应的方法。

    //发出通知的人,也就是通知中心,大Boss
    class Boss : SubjectType{
        func setInfo(_ info : String){
            if info != self.info {
                self.info = info
                self.notifyObserver()
            }
        }
        
        override func registerObserver(_ observer: ObserverType) {
            self.removeObserver(observer)
            self.observersArray.append(observer)
        }
        
        override func removeObserver(_ observer: ObserverType) {
            for i in 0..<self.observersArray.count {
                if type(of: observersArray[i]) == type(of: observer){
                    self.observersArray.remove(at: i)
                    break
                }
            }
        }
        override func notifyObserver() {
            for observer : ObserverType in self.observersArray {
                observer.update(self.info)
            }
        }
    }
    

    3)观察者的实现

    有两个观察者,分别是 Coder(程序员) 和PM(产品经理),都是ObserverType的子类,重写了 基类的 update()和display()方法。
    观察者在观察到 Subject 的info 被改变后,就会执行 update()方法。

    //程序员
    class Coder : ObserverType{
        override func update(_ info: String) {
            self.info = info
            display()
        }
        override func display() {
            print("程序员收到:\(self.info)")
        }
    }
    //产品经理
    class PM : ObserverType{
        override func update(_ info: String) {
            self.info = info
            display()
        }
        override func display() {
            print("产品经理收到:\(self.info)")
        }
    }
    

    4)测试用例

    //创建boss
        let bossSubject : Boss = Boss()
        //创建观察者
        let coderObserver : Coder = Coder()
        let PMObserver : PM = PM()
        //添加观察者
        bossSubject.registerObserver(coderObserver)
        bossSubject.registerObserver(PMObserver)
        print(bossSubject.observersArray)
        bossSubject.setInfo("第一次通知")
        //程序员走出了会议室(移除通知)
        bossSubject.removeObserver(coderObserver)
        bossSubject.setInfo("第二次通知")
    

    测试结果为:
    第一次发生通知的时候,两个都是观察者,接着 移除了coder的观察者,第二次发通知的时候,就只有pm是观察者了。
    在这里插入图片描述

    二、Foundation 框架中的通知

    Foundation框架是自带一套完成的 观察者模式机制的, 即 通知机制。

    1、NotificationCenter 简述

    NotificationCenter是Foundation框架通知机制中的通知中心,扮演者调度通知的作用。
    Subject 往通知中心发通知,有通知中心统一管理,把发送的消息分发给相应的观察者。
    所以通知中心是一个集合,集合中有多个 Subject 和多个 Observer的集合。
    通知中心扮演的角色就是将Subject 和 相应的 Observer进行关联。
    原理图如下所示:
    在这里插入图片描述

    2、Foundation 中通知的使用

    1)创建 Subject 通知者
    有以下几个步骤:
    (1)创建消息字典,该字典就是观察者所获取的信息
    (2)创建通知 Notification,该通知也是需要发送给 Observer的,通知中包括 Subject 的名称、发送通知的对象,以及创建的消息字典
    (3)将 通知 发送给 通知中心 NotificationCenter ,通知中心会根据 通知的信息找到观察此通知的 观察者Observer,并把通知传给每个观察者

    Boss扮演的就是Subject

    //subject
    class Boss : NSObject{
        func sendMessage(_ message : String){
            //1、创建消息字典
            let userInfo = ["message":message]
            //2、创建通知
            let notification = Notification.init(name: Notification.Name(rawValue: "Boss1"), object: nil, userInfo: userInfo)
            //3、发送通知
            NotificationCenter.default.post(notification)
        }
    }
    

    2)添加 Observer 观察者
    我们需要制定观察者所观察的 Subject,通过其名称来明确观察的对象,还需要指定收到通知后的执行方法,在指定的方法有一个参数,是用来接收发出的 通知Notification 的对象的。
    注意:在当前对象释放时,需要移除观察者

    //添加observer
    class Coder:NSObject{
        func observerBoss(){
            NotificationCenter.default.addObserver(self, selector: #selector(accepteNotification(_:)), name: NSNotification.Name(rawValue: "Boss"), object: nil)
        }
        @objc func accepteNotification(_ notification : Notification){
            let info = notification.userInfo
            print("收到老板通知了:\(String(describing: info!["message"]))")
        }
        deinit {
            NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: "Boss"), object: nil)
        }
    }
    
    

    3)测试用例

    let boss = Boss()
        let coder = Coder()
        coder.observerBoss()
        boss.sendMessage("涨工资啦")
    

    测试结果为在这里插入图片描述

    三、自定义通知中心

    实现一套属于自己的通知机制,调用方式类似于Foundation框架中的通知调用方式,只是命名不同。

    1、分析通知机制原理

    自定义通知机制的 类“类图”如下所示:
    在这里插入图片描述
    1)MyCustomNotificationCenter 对应 NotificationCenter
    MyCustomNotification 对应 Notification
    MySubject 和MyObserver 是为了实现该通知机制所创建的Subject 和Observer

    2)通知机制的运作方式
    Boss将Notification发送到NotificationCenter,然后NotificationCenter在通过其内部实现机制,将Boss发送过来的Notification发送到Coder。

    3)MyCustomNotification

    参数 说明
    name 发送通知对象的名称 ,即“Boss”
    object 上述例子的Boss的对象
    userInfo 发送给Observer的信息字典

    4)MyObserver

    参数 说明
    observer 观察者对象
    selector 观察者对象收到通知后要执行的方法

    5)MySubject

    参数 说明
    notification 记录要发送的通知
    observers 是一个数组,存储该subject对应的观察者
    函数 说明
    addCustomObserver 添加观察者
    removeCustomObserver 移除观察者
    postNotification 发送通知

    6)MyCustomNotificationCenter
    该类的对象是通过 defaultCenter() 方法获取的单例对象,在该方法中有一个 center 名称的字段,该字段是字典类型, key 对应MySubject 对象指定的name,value对应 Mysubject 对象。
    其中也有添加、移除观察者和发送通知等方法。

    2、Subject 和 Observer 实现

    1)MyCustomNotification

    参数 说明
    name 发送通知对象的名称 ,即“Boss”
    object 上述例子的Boss的对象
    userInfo 发送给Observer的信息字典
    //通知
    class MyCustomNotification:NSObject{
        let name : String
        let object : AnyObject?
        let userInfo : [NSObject : AnyObject]?
        
        init(_ name : String, _ object : AnyObject?, _ userInfo : [NSObject:AnyObject]?) {
            self.name = name
            self.object = object
            self.userInfo = userInfo
        }
    }
    

    2)MyObserver

    参数 说明
    observer 观察者对象
    selector 观察者对象收到通知后要执行的方法

    当收到通知时,就会执行observer的selector方法。

    //观察者
    class MyObserver:NSObject{
        let observer : AnyObject
        let selector : Selector
        
        init(_ observer : AnyObject, selector : Selector){
            self.observer = observer
            self.selector = selector
        }
    }
    

    3)MySubject
    MySubject类将Notification与Observers进行关联。
    具体说来就是当MySubject收到Notification中,就会遍历其所有的观察者(observers的类型是ObserveArray,其中存储的是MyObserver的对象),遍历观察者时就会去执行该观察者所对应的selector方法。

    参数 说明
    notification 存储的就是Subject所要发出的通知
    observers 数组类型,其中存储的是MyObserver的对象
    函数 说明
    addCustomObserver() 往observers数组中添加观察者
    removeCustomObserver() 移除observers数组中的观察者
    postNotification() 对observers数组进行遍历取出MyObserver的对象,然后执行该对象中的selector方法,并且将notification作为selector方法的参数
    typealias ObserverArray = Array<MyObserver>
    //主题
    class MySubject : NSObject{
        var notification : MyCustomNotification?
        var observers : ObserverArray
        
        init(_ notification : MyCustomNotification?, _ observers : ObserverArray){
            self.notification = notification
            self.observers = observers
        }
        
        //添加观察者
        func addCustomObserver(_ observer : MyObserver){
            for i in 0..<observers.count {
                if observers[i].observer === observer.observer{
                    return
                }
            }
            self.observers.append(observer)
        }
        
        //移除观察者
        func removeCustomObserver(_ observer : MyObserver){
            for i in 0..<observers.count {
                if observers[i].observer === observer.observer{
                    observers.remove(at: i)
                    break
                }
            }
        }
        
        //发送通知
        func postNotification(){
            for i in 0..<observers.count {
                let myObserver : MyObserver = self.observers[i]
                myObserver.observer.performSelector(inBackground: myObserver.selector, with: self.notification)
            }
        }
    }
    

    3、通知中心的实现

    通知中心的类的定义如下:

    typealias SubjectDictionary = Dictionary<String, MySubject>
    class MyCustomNotificationCenter : NSObject{
    }
    

    1)模拟系统通知中心的单例获取

    private static let singleton = MyCustomNotificationCenter()
        static func defaultCenter()->MyCustomNotificationCenter{
            return singleton
        }
        override init() {
            super.init()
        }
    

    2)通知中心发送通知的过程

    对应NotificationCenter.defaultCenter中的postNotification(notification)。

    (1)首先会调用getSubjectWithNotifaction(notification)方法来从center中获取可以发送该notification的Subject对象
    (2)在getSubjectWithNotifaction(notification)中,如果center中没有可以发送该notification的对象,那么就创建一个MySubject对象,并将该notification赋值给这个新建的MySubject对象,最后将我们创建的这个新的subject添加进center数组中
    (3)然后调用该subject对象的postNotification()方法即可

    //================通知中心,存储的是Subject对象的集合============
        private var center : SubjectDictionary = SubjectDictionary()
        
        //发出通知
        func postNotification(_ notification : MyCustomNotification){
            //首先会调用getSubjectWithNotifaction(notification)方法来从center中获取可以发送该notification的Subject对象
            let subject = self.getSubjectNotification(notification)
            //然后调用该subject对象的postNotification()方法即可
            subject.postNotification()
        }
        
        //根据notification获取相应的subject对象,没有的话就创建
        func getSubjectNotification(_ notification : MyCustomNotification)->MySubject{
            
            guard let subject = center[notification.name] else {
                //如果center中没有可以发送该notification的对象,那么就创建一个MySubject对象,并将该notification赋值给这个新建的MySubject对象,
                //最后将我们创建的这个新的subject添加进center数组中
                center[notification.name] = MySubject(notification, ObserverArray())
                return self.getSubjectNotification(notification)
            }
            if subject.notification == nil {
                subject.notification = notification
            }
            return subject
        }
    

    3)添加监听者

    对应NotificationCenter.defaultCenter中的addObserver()。

    (1)首先我们把传入的参数生成MyObserver的对象,
    (2)然后通过aName从center字典中获取相应的MySubject对象。如果center中没有对应的MySubject对象,我们就创建该对象,并且将该对象的notification属性暂且指定为nil。
    (3)最后调用MySubject类中的addCustomObserver()方法进行观察者的添加。

    //添加监听者
        func addObserver(_ observer : AnyObject, _ aSelector : Selector, _ aName : String){
            //首先我们把传入的参数生成MyObserver的对象
            let myObserver = MyObserver(observer, selector: aSelector)
            //通过aName从center字典中获取相应的MySubject对象
            var subject : MySubject? = center[aName]
            
            //如果center中没有对应的MySubject对象,我们就创建该对象,并且将该对象的notification属性暂且指定为nil
            if subject == nil {
                subject = MySubject(nil, ObserverArray())
                center[aName] = subject
            }
            
            //调用MySubject类中的addCustomObserver()方法进行观察者的添加
            subject?.addCustomObserver(myObserver)
        }
    

    4)移除监听者

    (1)首先也是通过name从center字典中获取MySubject的对象,(2)然后调用MySubject对象的removeCustomObserver()方法进行移除掉。

    //从通知中心移除observer
        func removeObserver(_ observer : AnyObject, _ name : String){
            //首先也是通过name从center字典中获取MySubject的对象
            guard let subject : MySubject = center[name] else {
                return
            }
            //然后调用MySubject对象的removeCustomObserver()方法进行移除掉
            subject.removeCustomObserver(observer as! MyObserver)
        }
    

    4、测试用例

    class Boss : NSObject{
        func sendMessage(_ message : String){
            //1、创建消息字典
            let userInfo = ["message":message]
            //2、创建通知
            let notification = MyCustomNotification.init(Notification.Name(rawValue: "Boss2").rawValue, nil, userInfo as [NSObject : AnyObject])
            //3、发送通知
            MyCustomNotificationCenter.defaultCenter().postNotification(notification)
        }
    }
    //添加observer
    class Coder:NSObject{
        func observerBoss(){
            MyCustomNotificationCenter.defaultCenter().addObserver(self, #selector(accepteNotification(_:)), "Boss2")
        }
        @objc func accepteNotification(_ notification : MyCustomNotification){
            let info : Dictionary = notification.userInfo!
            print("收到老板通知了:",info["message" as! NSObject])
        }
        deinit {
            MyCustomNotificationCenter.defaultCenter().removeObserver(self, "Boss2")
        }
    }
    
    
    let boss = Boss()
        let coder = Coder()
        let coder = Coder()
        coder.observerBoss()
        coder.observerBoss()
        boss.sendMessage("涨工资啦")
    

    测试结果与二中测试结果一致。

    展开全文
  • 观察者模式Swift中的应用 定义 观察者模式(又被称为发布-订阅(Publish/Subscribe)模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态...
  • 单例模式 定义:确保一个类只有一个实例,并提供一个全局访问点 简单来说就是 一个类在程序运行期间只能生成一个实例 一、使用GCD实现单例 1、swift 3.0以前 使用GCD中的dispatch_once来创建单例对象 class ...
  • 工厂模式是软件设计中应用非常多的一种模式,就不再赘述其原理和目的了! //: Playground - noun: a place where people can play import UIKit protocol Shape{ func draw() } class Rectangle: ...
  • From my short experience with Swift there are three approaches to implement the Singleton pattern that support lazy initialization and thread safety. These approaches might change or becom...
  • 中介者模式(The Mediator Pattern)   在软件工程中,中介者模式定义了一个对象封装了一些列对象的交互.这种模式被认为是一个行为模式,因为它可以改变程序的运行行为。也可以看这里。 通常一个程序是由大量的类...
  • 所以本文适合懂Swift语法,想快速了解23个设计模式大概在讲什么的同学。 前置知识 UML那些线 基本结构 比喻 让我联想到的一些事物 官方定义 原版定义 UML 不是原版UML 只保留了我觉得核心的部分 代码 Swift实现,...
  • /* 把统一复杂的工序、细分成不同的简单工序 能够轻易增加、修改工序 */ // 药品 复杂产品 class MedicineProduct { var p_stName : String = "" var p_mNumber : Int = 0 var p_stPacking : String = "" ...
  • 我们在重构代码时,往往会用到设计模式,文章的案例主要是来自《Head First Design Patterns》或者是参考其他博主的案例,需要下载pdf文件的,可以点击这个链接: 链接: 《Head First Design Patterns》中文pdf ...
  • swift-原型模式

    2018-02-23 16:35:42
    设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。GoF提出了23种设计模式,本系列将使用Swift语言来实现这些设计模式。概述通过复制一个已存在的对象来获得一个新的...
  • Swift中的模式匹配

    2015-12-02 17:11:21
    原文:Pattern Matching in Swift更新: 2015.9.19 包含关于该问题现有Swift语法的说明。 2015.9.25 添加关于标准库中现有的~>操作符的说明 其他文章系列 (1) Custom Pattern Matching(本篇) (2) Ranges and ...
  • Swift - 抽象工厂模式

    2018-07-17 15:33:44
    抽象工厂模式(Abstract Factory Pattern) 是一种比较常见的设计模式,其定义如下:Provide an interface for creating families of realted or dependent objects without specifying their concrete classes.(为...
  • 上一篇 设计模式(Swift) - 1.MVC和代理 中涉及到了三点,类图,MVC和代理模式. 类图用来清晰直观的表达设计模式. 作为Cocoa框架的核心结构模式,MVC怎样处理视图、模型、控制器之间的关系. 将我想做的事委托给有能力的...
  • /* 大宏药业门市售卖阿司匹林和扑热息痛商品。 学生享有95折优惠。 老人享有85折优惠。 VIP享有7折优惠。 把计算优惠控件、和选择哪种优惠控件进行解耦。...当增加新年优惠时、只需针对优惠控件编程即可。...
  • Swift µframework 实现观察者模式Observer pattern
  • 组合模式能各控件解耦、假如想增加抗组胺药生产部门、只需增加此部门即可。 */ class MedicineProduct { func addOrder(p_obMedicOrder : MedicineProduct) { } func removeOrder(p_mOrderNum : Int) { }
1 2 3 4 5 ... 20
收藏数 1,490
精华内容 596
关键字:

pattern模式 swift