在Swift中,许多面向对象编程语言的经验被概括和完善,通过组合(而不仅仅是继承)协议的能力。协议组合允许声明变量、函数参数或泛型,要求同时符合多个协议。这个机制在处理具有多个合同(接口)行为的对象时非常有用,同时灵活地避免多重继承的缺点。协议组合解决的问题是需要表达“对象必须满足一组要求”,而不仅仅是一个。
在Swift的实现中使用了特殊的语法:协议的组合用&符号(ampersand)表示,例如,protocolA & protocolB。在内部进行运行时检查(例如,在类型转换和泛型上下文中)。这最小化了类型的数量,并灵活地实现了“职责分离”模式。
代码示例:
protocol Drawable { func draw() } protocol Movable { func move() } struct Sprite: Drawable, Movable { func draw() { print("Sprite draws") } func move() { print("Sprite moves") } } func animate(object: Drawable & Movable) { object.draw() object.move() } let s = Sprite() animate(object: s)
关键特点:
可以创建类型为仅有protocolA & protocolB的变量,而不依赖于特定的结构或类吗?
是的,可以声明一个变量同时符合多个协议,例如:
var obj: protocolA & protocolB
但需要注意:如果组合中至少有一个协议限制为类类型(protocol: AnyObject),则这些变量只能引用对象(而不是值类型)。
可以将类类型包含在组合中,例如SomeClass & Drawable吗?
可以,但有细微之处:SomeClass & Protocol类型的组合要求值必须是该类(或其子类)的实例,并实现协议。这种方法用于限制泛型类型。
可以在协议扩展中将协议组合用作关联类型吗?
可以,但有一些限制:不能将associatedtype声明为组合,但可以在扩展中使用where来限制协议组合,例如,仅适用于符合多个协议的类型的扩展。
在项目中实现了5个相似的协议 — Drawable, Movable, Resizable, Colorable, Animatable。处处使用组合 Drawable & Movable & Resizable & Colorable & Animatable。常见错误伴随着复杂的bug,因为某些实体未实现其中一个合同。
优点:
缺点:
与复杂的组合相比,提炼出两个主要协议(例如Actor和Viewable),为组合"DynamicEntity"创建了类型别名,并在各处使用它。明确划分了职责。
优点:
缺点: