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

相关文章
相关标签/搜索