7-27.【高级特性】协议组合 (protocolA & protocolB) 与协议继承有什么区别?

4 阅读3分钟

协议组合 (Protocol Composition)协议继承 (Protocol Inheritance) 就像是“临时组队”与“家族传承”的区别。虽然它们最终都能让你同时拥有多个协议的能力,但在设计意图和类型约束上有本质差异。


1. 协议继承 (Protocol Inheritance)

协议继承是静态的、永久的。它在定义协议时就确定了层级关系。

  • 定义方式protocol C: A, B {}

  • 本质:创建一个新的协议。任何遵守 C 的类型,必须同时实现 AB 的要求。

  • 使用场景

    • 建立层级:当一个概念是另一个概念的子集或扩展时(例如 User 继承自 Person)。
    • 功能累加:你希望为这一组功能起一个有意义的“名字”。

Swift

protocol Readable { func read() }
protocol Writable { func write() }

// 协议继承:定义了一个新身份
protocol CodableStream: Readable, Writable { }

struct File: CodableStream {
    func read() { /*...*/ }
    func write() { /*...*/ }
}

2. 协议组合 (Protocol Composition)

协议组合是动态的、临时的。它不创建新类型,只是一种类型约束

  • 定义方式Readable & Writable

  • 本质:一种匿名协议簇。它告诉编译器:“我不在乎你是什么类型,只要你同时满足这两个协议就行。”

  • 使用场景

    • 函数参数:限制参数必须具备多种能力。
    • 临时约束:不需要为了一个简单的组合而专门定义一个庞大的协议层级。

Swift

// 协议组合:没有新名字,只是临时的约束
func syncData(source: Readable & Writable) {
    source.read()
    source.write()
}

3. 核心区别对比

特性协议继承 (protocol C: A, B)协议组合 (A & B)
产物创建了一个新的命名类型 C产生一个匿名的复合类型
耦合度。修改父协议可能影响整个继承树。。随用随组,不改变原协议结构。
身份 (Identity)类型必须明确声明遵守 C类型只要分别遵守了 AB 即可。
扩展性可以为 C 增加额外的方法要求。无法在组合中增加新要求,只能用已有的。
语义表达 "Is-a" 关系(是一个...)。表达 "Acts-like" 关系(表现得像...)。

4. 什么时候用哪个?

选协议继承的情况:

当你发现某个组合在你的代码库中反复出现,且它代表了一个独立的业务概念

例如:Swift.Codable 实际上就是 Decodable & Encodable 的继承。因为它太常用了,给它一个名字能极大地提高可读性。

选协议组合的情况:

当你在编写泛型工具函数或 UI 组件,且约束是高度特定于该场景时。

例如:一个保存按钮只需要一个 Encodable & Validatable 的对象。你没必要为此专门定义一个 protocol ValidatableEncodable


5. 进阶:组合中的 AnyObject

你经常会在协议组合中看到 AnyObject。这通常用于将协议约束限制在类 (Class) 上,常用于 delegate 模式以避免循环引用。

Swift

// 这要求对象既是类,又遵守该协议
weak var delegate: (MyDelegate & AnyObject)?