Swift-29-高级编程-协议详解

在学习了利用访问控制来隐藏信息。隐藏信息是封装(encapsulation)的一种形式,可以在设计软件时达到其中一部分的变化不影响其他部分的目的。Swift还提供另一种形式的封装:协议。协议可以让你无须知道类型本身的信息,就能指定并利用类型的接口(interface),接口是类型提供的一组属性和方法。

Swift中的协议和ObjectiveC中的协议的概念是一样的,主要目的是在不修改类结构的前提下扩展类的功能。一个类可以实现多个协议。但在实现方式上Swift中的协议更像java接口的概念。

本节会以一个复杂的例子来学习协议的基本内容,我们会在这个例子中打印下列这样的一个表格。

协议基本实现

协议定义

语法结构: protoclo proName{ properties..., function...}

用protocol关键字定义,可以定义属性和方法,首先定义一个名为TabularDataSource的协议。

swift 复制代码
protocol TabularDataSource {
    //定义协议属性和必须要实现的属性读取方法,get表示属性可读,也可以设置成{get set}
    var numberOfRows: Int { get }
    var numberOfColumns: Int { get }

    //定义协议接口方法,所有实现此协议的类必须要实现以下两个方法
    func label(forColumn column: Int) -> String //行标签
    func itemFor(row: Int, column: Int) -> String //单元格数据
}

上述定义有一点需要注意,因为协议可由类和结构体实现,但结构体中的方法默认是不能改改其self值的,如果需要修改,则可将协议的 func label(){} 函数定义修改为 mutaling func label(){}。

协议实现

语法格式:

  1. 结构体和类实现语法:struct strName:ProtocolName,在结构体名称后面加上冒号+协议名称,比如struct Department: TabularDataSource;
  2. 类实现语法定义:类分有父类和无父类两种情况,写法一样,由swift自已来识别,比如
    • 无父类:class className:protocolName1, protocolName2{};
    • 有父类:class className:SuperClass, protocolName1, protocolName2{};
  • 定义数据结构
swift 复制代码
struct Person {
    let name: String
    let age: Int
    let yearsOfExperience: Int
}
  • 实现 TabularDataSource 协议
swift 复制代码
//CustomStringConvertible是框架定义的一个协议,它里面只定义了一个名为description的属性,相当于toString方法
struct Department: TabularDataSource, CustomStringConvertible {
    let name: String
    var people = [Person]()

    var description: String {
        return "Department (\(name))"
    }

    init(name: String) {
        self.name = name
    }

    mutating func add(_ person: Person) {
        people.append(person)
    }

   //协议属性,必须实现
    var numberOfRows: Int {
        return people.count
    }

    var numberOfColumns: Int {
        return 3
    }

   //协议接口方法,必须实现
    func label(forColumn column: Int) -> String {
        switch column {
        case 0: return "Employee Name"
        case 1: return "Age"
        case 2: return "Years of Experience"
        default: fatalError("Invalid column!")
        }
    }

    //协议接口方法,必须实现
    func itemFor(row: Int, column: Int) -> String {
        let person = people[row]
        switch column {
        case 0: return person.name
        case 1: return String(person.age)
        case 2: return String(person.yearsOfExperience)
        default: fatalError("Invalid column!")
        }
    }
}

协议做为参数使用

把协议做为参数传递,CustomStringConvertible是另一个系统实义的协议,注意代码写法,这里需要用到 &关键字。

swift 复制代码
func printTable(_ dataSource: TabularDataSource & CustomStringConvertible) {
    print("Table: \(dataSource.description)")

    // 创建表格头
    var firstRow = "|"

    // 记录每一列的宽度
    var columnWidths = [Int]()

    for i in 0 ..< dataSource.numberOfColumns {
        let columnLabel = dataSource.label(forColumn: i)
        let columnHeader = " \(columnLabel) |"
        firstRow += columnHeader
        columnWidths.append(columnLabel.count)
    }

    print(firstRow)

    //拼装行数据
    for i in 0 ..< dataSource.numberOfRows {
        // 创建一个空符串
        var out = "|"

        // 拼接第一列数据到一行上
        for j in 0 ..< dataSource.numberOfColumns {
            let item = dataSource.itemFor(row: i, column: j)
            let paddingNeeded = columnWidths[j] - item.count
            let padding = repeatElement(" ", count: paddingNeeded).joined(separator: "")
            out += " \(padding)\(item) |"
        }

        print(out)
    }
}

协议测试

swift 复制代码
var department = Department(name: "Engineering")
department.add(Person(name: "Joe", age: 30, yearsOfExperience: 6))
department.add(Person(name: "Karen", age: 40, yearsOfExperience: 18))
department.add(Person(name: "Fred", age: 50, yearsOfExperience: 20))

printTable(department)

高级用法

协议继承

语法结构:协议支持协议的继承,这点和java的接口比较相似,并且可以多继承

swift 复制代码
protocol MyProtocol : ProtocolOne, ProtocolTwo{

}

这样上一节小的例子就可以定义成下列的结构,代码如下:

swift 复制代码
//定义协议-继承模式, CustomStringConvertible为系统提供的一个标准协议
protocol TabularDataSource: CustomStringConvertible {

}

//协议实现
struct Department: TabularDataSource{

}

//协议做为参数使用
func printTable(_ dataSource: TabularDataSource){

}

协议组合

对于上面继承的模式实现有时不是太建议,因为灵活性差一些。因为这样架构的话会把两个不相关的功能耦合在了一起。所以一般很少用到协议继承。所以最好用组合的方式改成如下代码实现。

  • 两个协议组合可以用&来连接,比如:TabularDataSource, CustomStringConvertible
  • 多个协议组合可用用<>来连接,比如:<TabularDataSource, CustomStringConvertible, threeAble>
swift 复制代码
//协议定义
protocol TabularDataSource{

}

//协议实现
struct Department: TabularDataSource, CustomStringConvertible{

}

//协议做为参数使用-组合模式
func printTable(_ dataSource: TabularDataSource & CustomStringConvertible) {

}
相关推荐
白子寰5 分钟前
【C++打怪之路Lv14】- “多态“篇
开发语言·c++
王俊山IT18 分钟前
C++学习笔记----10、模块、头文件及各种主题(一)---- 模块(5)
开发语言·c++·笔记·学习
为将者,自当识天晓地。20 分钟前
c++多线程
java·开发语言
小政爱学习!22 分钟前
封装axios、环境变量、api解耦、解决跨域、全局组件注入
开发语言·前端·javascript
小李飞刀李寻欢29 分钟前
Mac电脑如何解压rar压缩包
macos·rar·解压
Java小白笔记30 分钟前
Mac中禁用系统更新
macos
AndyFrank30 分钟前
mac crontab 不能使用问题简记
linux·运维·macos
Mac新人31 分钟前
一招解决Mac没有剪切板历史记录的问题
macos·mac
王拴柱35 分钟前
Mac保护电池健康,延长电池使用寿命的好方法
macos·mac
daa2036 分钟前
macos中安装和设置ninja
macos