Chapter 25: Protocol-Oriented Programming
大綱
Protocol-oriented benefits
Introducing protocol extensions
protocol extension跟protocol最大的差異就是可以進行內容的實作。
protocol TeamRecord {
var wins: Int { get }
var losses: Int { get }
var winningPercentage: Double { get }
}
// protocol extensions
extension TeamRecord {
// 替protocol新增變數,並實作內容
var gamesPlayed: Int {
return wins + losses
}
}
Default implementations
可以幫protocol本身的內容進行實作。
it differs in that winningPercentage is a member of the TeamRecord protocol itself whereas gamesPlayed isn’t. Implementing a member of a protocol in an extension creates a default implementation for that member.”
protocol TeamRecord {
var wins: Int { get }
var losses: Int { get }
var winningPercentage: Double { get }
}
// Default implementations
// 直接幫宣告protocol本身的winningPercentage進行實作
extension TeamRecord {
var winningPercentage: Double {
return Double(wins) / Double(wins + losses)
}
}
Understanding protocol extension dispatching
If a type defines a method or property in protocol extension, without declaring it in the protocol itself, static dispatching comes into play.
protocol WinLoss {
var wins: Int { get }
var losses: Int { get }
}
extension WinLoss {
var winningPercentage: Double {
return Double(wins) / Double(wins + losses)
}
}
struct CricketRecord: WinLoss {
var wins: Int
var losses: Int
var draws: Int
// winningPercentage並沒有直接宣告WinLoss之中,而是宣告並實作在WinLoss的extension中
var winningPercentage: Double {
return Double(wins) / Double(wins + losses + draws)
}
}
let miamiTuples = CricketRecord(wins: 8, losses: 7, draws: 1)
let winLoss: WinLoss = miamiTuples
miamiTuples.winningPercentage // 0.5, 優先使用CricketRecord中已實作的方法
winLoss.winningPercentage // 0.53, 使用WinLoss的extension中實作的方法
Type constraints
透過type constraint可以讓你在某個protocol中去使用另外其他protocol的東西。
By using a type constraint on a protocol extension, you’re able to use methods and properties from another type inside the implementation of your extension.
protocol PostSeasonEligible {
var minimumWinsForPlayoffs: Int { get }
}
// Type Constraints
extension TeamRecord where Self: PostSeasonEligible {
var isPlayoffEligible: Bool {
// TeamRecord中可以使用 PostSeasonEligible中宣告的minimumWinsForPlayoffs
return wins > minimumWinsForPlayoffs
}
}
protocol Tieable {
var ties: Int { get }
}
// Type Constraints
extension TeamRecord where Self: Tieable {
var winningPercentage: Double {
// TeamRecord中可以使用 Tieable中宣告的ties
return Double(wins) / Double(wins + losses + ties)
}
}
struct RugyRecord: TeamRecord, Tieable {
var wins: Int
var losses: Int
var ties: Int
}
let rugbyRecord = RugyRecord(wins: 8, losses: 7, ties: 1)
rugbyRecord.winningPercentage // 0.5
Programming to interfaces, not implementations
Traits, mixins and multiple inheritance
Mixins 和 Traits 的概念1由此引入。
通过继承,你定义你的类是什么。例如每条
Dog
都是一个Animal
。通过 Traits,你定义你的类能做什么。例如每个
Animal
都能eat()
,但是人类也可以吃,而且异世奇人(Doctor Who)也能吃鱼条和蛋挞,甚至即使是位 Gallifreyan(既不是人类也不是动物)。
使用 Traits,重要的不是「是什么」,而是能「做什么」。
继承描述了一个对象是什么,而 Traits 描述了这个对象能做什么。
Traits 很赞的一点就是它们并不依赖于使用到它们的对象本身的身份。Traits 并不关心类是什么,亦或是类是从哪里继承的:Traits 仅仅在类上定义了一些函数。
异世奇人(Doctor Who)可以既是一位时间旅行者,同时还是一个外星人;而爱默·布朗博士(Dr Emmett Brown)既是一位时间旅行者,同时还属于人类;钢铁侠(Iron Man)是一个能飞的人,而超人(Superman)是一个能飞的外星人。
protocol TieableRecord {
var ties: Int { get }
}
protocol DivisionalRecord {
var divisionalWins: Int { get }
var divisionalLosses: Int { get }
}
protocol ScoreableRecord {
var totalPoints: Int { get }
}
extension ScoreableRecord where Self: TieableRecord, Self: TeamRecord {
var totalPoints: Int {
return (2 * wins) + (1 * ties)
}
}
// Traits,重要的不是「是什么」,而是能「做什么」
struct NewHockeyRecord: TeamRecord, TieableRecord, DivisionalRecord, CustomStringConvertible, Equatable {
var wins: Int
var losses: Int
var ties: Int
var divisionalWins: Int
var divisionalLosses: Int
var description: String {
return "\(wins) - \(losses) - \(ties)"
}
}
Simplicity
Why Swift is a protocol-oriented language
先以Swift中Array當做例子,如何利用POP來定義出Array
Array is a struct means it’s a value type
it can’t be subclassed nor can it be a superclass.
Array will get numerous properties and methods common to every Collection, such as first, count or isEmpty
you can split() an Array or find the index(of:) an element, assuming the type of that element conforms to Equatable
// From the Swift standard library
public struct Array<Element> : RandomAccessCollection, MutableCollection {
// ...
}
Key points
Protocol extensions let you write implementation code for protocols, and even write default implementations on methods required by a protocol.
Protocol extensions are the primary driver for protocol-oriented programming and let you write code that will work on any type that conforms to a protocol.
Type constraints on protocol extensions provide additional context and let you write more specialized implementations.
You can decorate a type with traits and mixins to extend behavior without requiring inheritance.
Last updated
Was this helpful?