最近在项目中用到了 Swift 的 Protocol Composition(协议组合),觉得这真是一个不错的设计,于是来简单的聊一聊我是怎么用它的。
Protocol Composition,中文叫「协议组合」或「协议合成」,简单地来说就是把不同的协议合起来以使目标对象限定在满足全部协议的一种方式。它的用法也蛮简单的,官方文档的这个例子可以很明了地学会怎么使用它:
一个叫 Named 的协议,提供 name (名字)这个属性,一个叫 Aged 的协议,提供 age(年龄)这个属性,然后可以用 & 这个操作符,用 Named & Aged,就可以使传入的参数即满足 Named 又满足 Aged 了。
protocol Named {
var name: String { get }
}
protocol Aged {
var age: Int { get }
}
struct Person: Named, Aged {
var name: String
var age: Int
}
func wishHappyBirthday(to celebrator: Named & Aged) {
print("Happy birthday, \(celebrator.name), you're \(celebrator.age)!")
}
let birthdayPerson = Person(name: "Malcolm", age: 21)
wishHappyBirthday(to: birthdayPerson)
// Prints "Happy birthday, Malcolm, you're 21!"
可以说,利用 Protocol Composition 可以把协议分得非常细,通过灵活的组合来满足特定要求。
另外,利用 typealias 可以把组合后的协议也定一个名字,方便在其他地方大量使用:
typealias NamedAgedObject = Named & Aged
可能很多人像我一样看文档看到此为止,我之前没注意到的一个用法是,或者说被「Protocol Composition 」这个名字所迷惑的了,「&」这个操作符可不仅仅能组合协议而已,也能组合「Type + Protocol」(类型+协议)。
比如我在项目中,定义了一个协议叫 ScrollableContainer,它表示一个容器,包含一个 UIScrollView。
protocol ScrollableContainer {
var scrollView: UIScrollView { get }
}
我在其中一个地方,要使它可以用在有 scrollView 的 UIViewController 里,如何做这个的描述而不用创建一个 UIViewController 的子类呢?这就可以用 & 操作符来连接 Type + Protocol:
typealias ScrollableViewController = ScrollableContainer & UIVIewController
这样,凡是在需要接受一个带 UIScrollView 的 UIViewController 的参数的情况下,就可以直接传入 ScrollableViewController 这个新协议了。尽管它严格意义上不是两个 Protocol 的 Composition,而是一个 Type 和一个 Protocol 的 Composition。
总结:看文档时常常会错过一些小细节,或者没用到这个特性时,即使看过去也不会有印象,导致之后没用到这个特性。所以要温故知新啊~