美文网首页Swift快速上手
【《Pro Swift》阅读笔记】08 - 设计模式

【《Pro Swift》阅读笔记】08 - 设计模式

作者: Lebron_James | 来源:发表于2018-01-19 13:23 被阅读97次

书籍链接:《Pro Swift》 (链接需要梯子才能打得开)。

一、面向协议编程 (protocol-oriented programming(POP))

Swift给我们带来了一种新的设计模式,面向协议编程(后面用POP表示)。他继承了面向对象(后面用OOP表示)的封装和多肽特性,但是没有继承特性。POP让我们以横向的方式构建应用,而不是纵向:通过实现协议来添加方法或者属性,而不是通过继承。

二、协议扩展细节

1. 为协议方法提供默认的实现

在创建了一个协议之后,如果我们想给协议的某个方法提供默认的实现,可以用扩展来实现:

protocol Payable {
   func calculateWages() -> Int
}

extension Payable {
   func calculateWages() -> Int {
      return 10000
   }
}

2. Objective-C无法访问协议的默认实现

例如,刚刚那个例子,我们在协议加上@objc,让Objective-C上可以访问到Payable协议:

@objc protocol Payable {
    func calculateWages() -> Int
}

extension Payable {
    func calculateWages() -> Int {
        return 10000
    }
}

虽然我们用扩展为calculateWages()提供了默认实现,但是这个实现在Objective-C中是访问不到的,Objective-C必须自己实现这个方法。

3. 一个容易出现的错误

我们先看代码:

我们把协议里面的calculateWages()注释掉,然后扩展Payable实现了calculateWages()方法;定义了Surgeon并用遵循Payable协议,用扩展重写PauyablecalculateWages()方法;最后创建了两个实例gregorydoogie。结果是gregory.calculateWages()返回20000,而doogie.calculateWages()返回10000

即使gregorydoogie都是Surgeon的实例,但是doogie是以Payable的类型存储的。因为我们在payable协议的创建里把calculateWages()注释掉了,所以Swift在调用calculateWages()的时候是以他们存储的类型为准的,gregory的存储类型是Sugreon,所以调用Sugreon的方法,返回20000;而doogie是以Payable的类型存储的,所以调用Payable的方法,返回10000

如果我们恢复payable协议的calculateWages(),他们的结果都返回20000

三、横向思考

文章开始我们提到POP让我们以横向的方式构建应用,而不是纵向。纵向就意味着继承,先有一个基类,然后再不断地创建子类;而横向则是通过创建协议,然后通过实现协议的规定,添加特定的方法。我们可以实现多个协议,这听起来就像多继承。

四、POP练习

首先我们先定义六个协议,每个协议有一个方法,并且都提供了默认的实现:

protocol Payable {
    func calculateWages() -> Int
}

extension Payable {
    func calculateWages() -> Int {
        return 10000
    }
}

// 提供治疗
protocol ProvidesTreatment {
    func treat(name: String)
}

extension ProvidesTreatment {
    func treat(name: String) {
        print("I have treated \(name)")
    }
}

// 提供诊断
protocol ProvidesDiagnosis {
    func diagnose() -> String
}

extension ProvidesDiagnosis {
    func diagnose() -> String {
        return "He's dead, Jim"
    }
}

// 进行手术
protocol ConductsSurgery {
    func performSurgery()
}

extension ConductsSurgery {
    func performSurgery() {
        print("Success!")
    }
}

// 休息
protocol HasRestTime {
    func takeBreak()
}

extension HasRestTime {
    func takeBreak() {
        print("Time to watch TV")
    }
}

// 学习
protocol NeedsTraining {
    func study()
}

extension NeedsTraining {
    func study() {
        print("I'm reading a book")
    }
}

再创建四个医院里的角色:

struct Receptionist { } // 接待员
struct Nurse { }        // 护士
struct Doctor { }       // 医生
struct Surgeon { }      // 外科医生

然后让刚刚创建的角色遵循合适的协议:

extension Receptionist: Payable, HasRestTime, NeedsTraining {}

extension Nurse: Payable, HasRestTime, NeedsTraining, ProvidesTreatment {}

extension Doctor: Payable, HasRestTime, NeedsTraining, ProvidesTreatment, ProvidesDiagnosis {}

extension Surgeon: Payable, HasRestTime, NeedsTraining, ProvidesDiagnosis, ConductsSurgery {}

但是我们可以看到,所有的角色都遵循了PayableHasRestTimeNeedsTraining,上面的代码就重复了,所以我们可以再创建一个新的协议把那三个重复的协议组合起来:

protocol Employee: Payable, HasRestTime, NeedsTraining {}

extension Receptionist: Employee {}
extension Nurse: Employee, ProvidesTreatment {}
extension Doctor: Employee, ProvidesDiagnosis,ProvidesTreatment {}
extension Surgeon: Employee, ProvidesDiagnosis, ConductsSurgery{}

对扩展进行约束

例如,我们想在Employee添加一个方法:

extension Employee {
   func checkInsurance() {
      print("Yup, I'm totally insured")
   }
}

但是checkInsurance()方法并不是所有遵循了Employee的类型都适合的,所以我们要限制那些类型可以使用checkInsurance()方法。例如我们只想让ProvidesTreatment这个类型拥有checkInsurance()方法,则可以用where来限制:

extension Employee where Self: ProvidesTreatment {
   func checkInsurance() {
      print("Yup, I'm totally insured")
   }
}

五、MVC和MVVM

作者还另外介绍了MVC和MVVM,关于这两个概念的文章已经非常多,我就不在这写了。大家可以自行了解。

有任何问题,欢迎大家留言!

欢迎加入我管理的Swift开发群:536353151,本群只讨论Swift相关内容。

原创文章,转载请注明出处。谢谢!

相关文章

网友评论

    本文标题:【《Pro Swift》阅读笔记】08 - 设计模式

    本文链接:https://www.haomeiwen.com/subject/qdpooxtx.html