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) {

}
相关推荐
C-SDN花园GGbond1 小时前
【探索数据结构与算法】插入排序:原理、实现与分析(图文详解)
c语言·开发语言·数据结构·排序算法
迷迭所归处2 小时前
C++ —— 关于vector
开发语言·c++·算法
架构文摘JGWZ3 小时前
Java 23 的12 个新特性!!
java·开发语言·学习
leon6253 小时前
优化算法(一)—遗传算法(Genetic Algorithm)附MATLAB程序
开发语言·算法·matlab
锦亦之22334 小时前
QT+OSG+OSG-earth如何在窗口显示一个地球
开发语言·qt
我是苏苏4 小时前
Web开发:ABP框架2——入门级别的增删改查Demo
java·开发语言
姜太公钓鲸2334 小时前
c++ static(详解)
开发语言·c++
菜菜想进步4 小时前
内存管理(C++版)
c语言·开发语言·c++
2301_789985944 小时前
Java语言程序设计基础篇_编程练习题*18.29(某个目录下的文件数目)
java·开发语言·学习
知星小度S4 小时前
C语言——自定义类型
c语言·开发语言