Swift: 帶有關聯類型的協議是什麼樣的?

做者:Natasha The Robot,原文連接,原文日期:2016-07-28
譯者:jseanj;校對:saitjr;定稿:CMBgit

最近我作了一個關於帶有關聯類型的協議(PATs, Protocols with Associated Types)的演講,我原本還以爲觀衆對這個已經耳熟能詳了,但事實卻相反。github

不少人並不知道 PATs 是什麼——這我應該預料到的,由於我自學就用了一段時間。所以我想當面講解下,尤爲是這些東西比較難理解,並且我也沒能找到很好的解釋。編程

Gwendolyn Weston 在東京 try! Swift 大會上給出的解釋對我頗有幫助(視頻在這)。所以文中的示例是受她的演講啓發。Pokemon 將會出現...swift

在 PATs 以前

目前我在 Pokemon Go 中是 9 級,我據說(感謝個人私人教練 @ayanonagon)全部的 Pokemon 有一些共同的特徵,好比攻擊能力。app

對於從 Objective-C 或者其餘面嚮對象語言遷移過來的人來講,使用一個具備全部共同功能的 Pokemon 子類是吸引人的。因爲每個 Pokemon 具備不一樣的攻擊能力——光、水或者火等等——咱們能夠在咱們的子類中使用泛型。ide

// 咱們必須確保泛型 Power 有初始化方法
protocol Initializable {
    init()
}
 
// Pokemon 子類
// 每個 Pokemon 有一個不一樣的 Power, 
// 所以 Power 是泛型
class Pokemon<Power: Initializable> {
    
    func attack() -> Power {
        return Power()
    }
}

此時,咱們有不一樣的 Power 類型:工具

struct ?: Initializable { // 實現 }
struct ?: Initializable { // 實現 }
struct ?: Initializable { // 實現 }

如今,其餘的 Pokemon 能夠從咱們的 Pokemon 基類繼承,而後他們自動的包含了攻擊方法!學習

class Pikachu: Pokemon<?> {}
class Vaporeon: Pokemon<?> {}
 
let pikachu = Pikachu()
pikachu.attack() // ?
 
let vaporeon = Vaporeon()
vaporeon.attack() // ?

問題是咱們使用的是繼承。若是你看了 Dave Abrahams 在 WWDC 上的 Swift 中面向協議編程,你如今的腦海裏看到的應該是 Crusty 的臉....net

使用繼承的問題是雖然剛開始的意圖是好的,但最終隨着意外的發生事情會變得愈來愈糟(好比 Pokemon Eggs 不能攻擊)。爲了你們更好的理解,我強烈推薦你們讀讀 Matthijs Hollemans 的 Mixins and Traits in Swift 2.0翻譯

畢竟,就像 Dave Abrahams 所說的,Swift 是一門面向協議的語言,因此咱們須要改變面向對象的思惟模式。

你好,PATs

讓咱們用 PATs 來代替繼承!相比於繼承全部東西,咱們能夠建立一個專一於 Pokemon 攻擊能力的協議。記住,因爲每個 Pokemon 有不一樣的 Power,所以咱們須要把它變成泛型。

protocol PowerTrait {
    // 就是這樣!關聯類型只是協議中表示泛型的一種語法
    associatedtype Power: Initializable
    
    func attack() -> Power
}
 
extension PowerTrait {
    // 經過協議擴展,咱們如今有一個默認的攻擊方法 
    func attack() -> Power {
        return Power()
    }
}

如今,每個遵循 PowerTrait 協議的 Pokemon 沒必要繼承就會具備攻擊能力了。

struct Pikachu: PowerTrait {
    // 因爲咱們使用的是默認的攻擊方法,就像在繼承時咱們指定了泛型同樣,咱們也必須指定關聯類型的類型
    // 注意,這仍然被稱爲 typealias,可是在 Swift 的將來版本中會變成 associatedtype
    associatedtype Power = ?
}
let pikachu = Pikachu()
pikachu.attack() //?
 
struct Vaporeon: PowerTrait {
    // 當 attack 方法被重寫後,
    // 基於方法標識,? 會被推斷爲關聯類型
    func attack() -> ? {
        // 自定義的攻擊邏輯
        return ?()
    }
}
let vaporeon = Vaporeon()
vaporeon.attack() //?

總結

就是這樣!帶有關聯類型的協議對於支持泛型的協議是一個新奇的術語。經過使用 PATs 這樣強有力的工具咱們得到了優雅的組合而不是糟糕的繼承。

爲了更多的瞭解 PATs 的限制以及更深刻的學習,我強烈推薦 Alexis Gallagher 的演講

玩得愉快。

本文由 SwiftGG 翻譯組翻譯,已經得到做者翻譯受權,最新文章請訪問 http://swift.gg

相關文章
相關標籤/搜索