协议扩展是在Swift中引入的,用于支持面向协议的编程理念:程序员可以直接在协议层面上引入默认方法的实现,而不是通过基类或全局函数。这减少了代码重复并允许灵活地调整类型的行为。
问题出现在默认实现掩盖了需要自定义(重写)的必要性,或者责任线变得模糊——特别是当一个类型实现多个有交集的协议时。此外,重要的是要记住:如果在类型本身中实现了方法,它将始终覆盖扩展中的实现。
解决方案是仅在协议扩展中使用通用行为,而在特定情况下明确实现类型内的方法。最好避免在同一个协议的两个扩展中重载相同的方法。
示例代码:
protocol Flyer { func fly() } extension Flyer { func fly() { print("默认飞行") } } struct Bird: Flyer {} let sparrow = Bird() sparrow.fly() // 输出:默认飞行
关键特性:
协议扩展可以为协议添加存储属性吗?
不可以,协议扩展只能添加计算属性和方法。存储属性是不允许的。
如果协议和类型有不同实现的方法同名,会发生什么?哪个会被调用?
当直接调用类型时,会调用类型的实现;通过协议类型引用调用实例时,会调用协议扩展的实现。
protocol Greeter { func greet() } extension Greeter { func greet() { print("来自扩展的问候") } } struct Person: Greeter { func greet() { print("来自类型的问候") } } let person = Person() person.greet() // 来自类型的问候 let greeter: Greeter = person greeter.greet() // 来自扩展的问候
可以在协议扩展中使用where约束吗?
可以。这是一个强大的特性——协议可以仅拓展特定类型。
extension Collection where Element: Equatable { func allEqual() -> Bool { guard let first = self.first else { return true } return allSatisfy { $0 == first } } }
负面案例
团队决定通过协议扩展实现错误日志记录,而没有考虑到每个服务可能想要添加其特定格式。结果不同的服务通过协议引用调用函数,行为逻辑与预期相差很大。
优点:
缺点:
积极案例
仅在行为始终通用的情况下扩展协议。对于特定情况——在类型中明确实现方法,代码审查对冲突点进行审核。
优点:
缺点: