文章目录
- 协议
- 协议继承
- 扩展
- 协议扩展
- 面向协议的编程
- 总结:
今天你将学习一些真正的 Swifty 功能:协议和面向协议的编程(POP)。
POP 摒弃了庞大而复杂的继承层次结构,代之以更小、更简单、可以组合在一起的协议。这确实应验了 Tony Hoare 多年前说过的一句话:“每个大型程序中,都有一个小程序试图脱颖而出。”
协议
协议是一种描述事物必须具有哪些属性和方法的方式。然后,您可以告诉 Swift 哪些类型使用该协议 - 这一过程称为采用或遵守协议。
例如,我们可以编写一个函数来接受具有属性的内容id,但我们并不关心具体使用哪种类型的数据。我们首先创建一个Identifiable协议,该协议要求所有符合要求的类型都有一个id可以读取(“get”)或写入(“set”)的字符串:
protocol Identifiable {var id: String { get set }
}
我们无法创建该协议的实例 - 它是我们想要的描述,而不是我们可以直接创建和使用的东西。但我们可以创建一个符合它的结构:
struct User: Identifiable {var id: String
}
最后,我们将编写一个displayID()接受任何Identifiable对象的函数:
func displayID(thing: Identifiable) {print("My ID is \(thing.id)")
}
协议继承
一个协议可以从另一个协议继承,这个过程称为协议继承。与类不同,您可以同时从多个协议继承,然后再在上面添加自己的自定义项。
我们将定义三个协议:Payable要求符合类型的实现一个calculateWages()方法、NeedsTraining要求符合类型的实现一个study()方法、HasVacation要求符合类型的实现一个takeVacation()方法:
protocol Payable {func calculateWages() -> Int
}protocol NeedsTraining {func study()
}protocol HasVacation {func takeVacation(days: Int)
}
现在我们可以创建一个Employee协议,将它们整合到一个协议中。我们不需要在顶部添加任何内容,因此我们只需编写开括号和闭括号即可:
protocol Employee: Payable, NeedsTraining, HasVacation { }
现在我们可以让新类型遵守单一协议,而不是遵守三个单独的协议。
扩展
扩展允许您向现有类型添加方法,使它们执行最初设计时未考虑的操作。
例如,我们可以向该Int类型添加一个扩展,使其具有squared()返回当前数字乘以自身的方法:
extension Int {func squared() -> Int {return self * self}
}
要尝试一下,只需创建一个整数,您就会看到它现在有一个squared()方法:
let number = 8
number.squared()
Swift 不允许在扩展中添加存储属性,因此您必须使用计算属性。例如,我们可以向isEven整数添加一个新的计算属性,如果它包含偶数,则返回 true:
extension Int {var isEven: Bool {return self % 2 == 0}
}
协议扩展
协议允许您描述某个对象应该具有哪些方法,但不提供其中的代码。扩展允许您提供方法中的代码,但仅影响一种数据类型 - 您无法同时将该方法添加到多种类型中。
协议扩展解决了这两个问题:它们就像常规扩展,不同之处在于您扩展的是特定类型,就像Int扩展整个协议一样,以便所有符合类型的类型都能获得您的更改。
例如,这是一个包含一些名称的数组和集合:
let pythons = ["Eric", "Graham", "John", "Michael", "Terry", "Terry"]
let beatles = Set(["John", "Paul", "George", "Ringo"])
Swift 的数组和集合都符合名为的协议Collection,因此我们可以对该协议进行扩展,以添加一个summarize()方法来整齐地打印集合
extension Collection {func summarize() {print("There are \(count) of us:")for name in self {print(name)}}
}
和Array现在Set都有该方法,因此我们可以尝试一下:
pythons.summarize()
beatles.summarize()
面向协议的编程
协议扩展可以为我们自己的协议方法提供默认实现。这使得类型更容易遵循协议,并允许一种称为“面向协议的编程”的技术 - 围绕协议和协议扩展编写代码。
首先,这里有一个名为的协议Identifiable,它要求任何符合要求的类型都具有id属性和identify()方法:
protocol Identifiable {var id: String { get set }func identify()
}
我们可以让每个符合要求的类型编写自己的identify()方法,但是协议扩展允许我们提供一个默认值:
extension Identifiable {func identify() {print("My ID is \(id).")}
}
现在,当我们创建一个符合Identifiable它的类型时,会identify()自动获得:
struct User: Identifiable {var id: String
}let twostraws = User(id: "twostraws")
twostraws.identify()
总结:
让我们总结一下:
- 协议描述了符合类型必须具有的方法和属性,但不提供这些方法的实现。
- 您可以在其他协议之上构建协议,类似于类。
- 扩展允许您向特定类型添加方法和计算属性,例如Int。
- 协议扩展允许您向协议添加方法和计算属性。
- 面向协议的编程是将应用程序架构设计为一系列协议的实践,然后使用协议扩展来提供默认方法实现。