作者:Natasha The Robot,原文鏈接,原文日期:2016-07-28
譯者:jseanj;校對:saitjr;定稿:CMB
最近我做了一個關于帶有關聯(lián)類型的協(xié)議(PATs, Protocols with Associated Types)的演講,我本來還覺得觀眾對這個已經(jīng)耳熟能詳了,但事實卻相反。
很多人并不知道 PATs 是什么——這我應該預料到的,因為我自學就用了一段時間。因此我想當面講解下,尤其是這些東西比較難理解,而且我也沒能找到很好的解釋。
Gwendolyn Weston 在東京 try! Swift 大會上給出的解釋對我很有幫助(視頻在這)。因此文中的示例是受她的演講啟發(fā)。Pokemon 將會出現(xiàn)...
在 PATs 之前
目前我在 Pokemon Go 中是 9 級,我聽說(感謝我的私人教練 @ayanonagon)所有的 Pokemon 有一些共同的特征,比如攻擊能力。
對于從 Objective-C 或者其他面向對象語言遷移過來的人來說,使用一個具有所有共同功能的 Pokemon 子類是吸引人的。由于每一個 Pokemon 具有不同的攻擊能力——光、水或者火等等——我們可以在我們的子類中使用泛型。
// 我們必須確保泛型 Power 有初始化方法
protocol Initializable {
init()
}
// Pokemon 子類
// 每一個 Pokemon 有一個不同的 Power,
// 因此 Power 是泛型
class Pokemon<Power: Initializable> {
func attack() -> Power {
return Power()
}
}
此時,我們有不同的 Power 類型:
struct ??: Initializable { // 實現(xiàn) }
struct ??: Initializable { // 實現(xiàn) }
struct ??: Initializable { // 實現(xiàn) }
現(xiàn)在,其他的 Pokemon 可以從我們的 Pokemon 基類繼承,然后他們自動的包含了攻擊方法!
class Pikachu: Pokemon<??> {}
class Vaporeon: Pokemon<??> {}
let pikachu = Pikachu()
pikachu.attack() // ??
let vaporeon = Vaporeon()
vaporeon.attack() // ??
問題是我們使用的是繼承。如果你看了 Dave Abrahams 在 WWDC 上的 Swift 中面向協(xié)議編程,你現(xiàn)在的腦海里看到的應該是 Crusty 的臉...
使用繼承的問題是雖然剛開始的意圖是好的,但最終隨著意外的發(fā)生事情會變得越來越糟(比如 Pokemon Eggs 不能攻擊)。為了大家更好的理解,我強烈推薦大家讀讀 Matthijs Hollemans 的 Mixins and Traits in Swift 2.0。
畢竟,就像 Dave Abrahams 所說的,Swift 是一門面向協(xié)議的語言,所以我們需要改變面向對象的思維模式。
你好,PATs
讓我們用 PATs 來代替繼承!相比于繼承所有東西,我們可以創(chuàng)建一個專注于 Pokemon 攻擊能力的協(xié)議。記住,由于每一個 Pokemon 有不同的 Power,因此我們需要把它變成泛型。
protocol PowerTrait {
// 就是這樣!關聯(lián)類型只是協(xié)議中表示泛型的一種語法
associatedtype Power: Initializable
func attack() -> Power
}
extension PowerTrait {
// 通過協(xié)議擴展,我們現(xiàn)在有一個默認的攻擊方法
func attack() -> Power {
return Power()
}
}
現(xiàn)在,每一個遵循 PowerTrait 協(xié)議的 Pokemon 不必繼承就會具有攻擊能力了。
struct Pikachu: PowerTrait {
// 由于我們使用的是默認的攻擊方法,就像在繼承時我們指定了泛型一樣,我們也必須指定關聯(lián)類型的類型
// 注意,這仍然被稱為 typealias,但是在 Swift 的未來版本中會變成 associatedtype
associatedtype Power = ??
}
let pikachu = Pikachu()
pikachu.attack() //??
struct Vaporeon: PowerTrait {
// 當 attack 方法被重寫后,
// 基于方法標識,?? 會被推斷為關聯(lián)類型
func attack() -> ?? {
// 自定義的攻擊邏輯
return ??()
}
}
let vaporeon = Vaporeon()
vaporeon.attack() //??
總結
就是這樣!帶有關聯(lián)類型的協(xié)議對于支持泛型的協(xié)議是一個新奇的術語。通過使用 PATs 這樣強有力的工具我們獲得了優(yōu)雅的組合而不是糟糕的繼承。
為了更多的了解 PATs 的限制以及更深入的學習,我強烈推薦 Alexis Gallagher 的演講。
玩得愉快。
本文由 SwiftGG 翻譯組翻譯,已經(jīng)獲得作者翻譯授權,最新文章請訪問 http://swift.gg。