设计模式之组合模式
背景
随着项目的工程结构越来越庞大,每个类内部都有自己的子组件,这个时候,常见的一种需求情况是,我希望这个系统中的一个类的子组件执行一个方法,并且,子组件的子组件也要执行这个方法。这个时候一般就会用到今天的主题——组合模式。
概念
什么是组合模式
组合模式是把一组对象当作一个单一的对象。比如说我们有一个如下的代码层级:
class A: NSObject {
var subNode = [B(), B()]
}
class B: NSObject {
var subNode = [C(), C()]
}
class C: NSObject {
}
这个代码层级中,A类包含了两个B类,而B类包含了两个C类,那么组合模式就是把这包含了4个C类的A类当作一个C类来使用。
用途
让外部调用者可以不用关心对象的层级结构,使用者对单个对象和组合对象的使用具有一致性。
什么叫一致性呢,举例:我希望B和B的所有子组件都具有一个功能(例如说‘hello’),但是,我不希望我让B和B的子组件执行功能时外部调用者不仅让B说hello,而且还要让B的子组件说hello。组合模式的用途就是,用户让C说hello需要
C().hello()
那么用户让B和B的所有子组件说hello只需要
B().hello()
用户使用C和使用B的方式方法是一样的,这就叫一致性。
实际应用
我们以代码的形式来看看组合模式如何实现我们上边所述的功能。我们假设说hello这个功能就是一个简单的print()
那么因为ABC都要实现这个功能,可以考虑将方法放到一个protocol
中:
protocol Say: AnyObject {
func hello()
}
那么对于类C来说,它的hello
方法很简单:
func hello() {
print(self.description)
}
对于类B来说,我们首先要让B打印它自己:
class B: NSObject, Say {
func hello() {
var subNode = [C(), C()]
print(self.description)
}
}
然后我们需要让B的每个子组件也打印它本身:
class B: NSObject, Say {
var subNode = [C(), C()]
func hello() {
print(self.description)
subNode.forEach { $0.hello() }
}
}
同样,类A也是同样的实现:
class A: NSObject, Say {
var subNode = [B(), B()]
func hello() {
print(self.description)
subNode.forEach { $0.hello() }
}
}
这样当我们执行
A().hello()
的时候,所有A的子组件也都打印出来了。使用者就不必关心A的内部结构了。打印一组对象和打印一个对象的操作是一样的。
总结
所谓的组合模式,就是将一组对象,当作一个对象来处理,这组对象都有一个共同的功能或者特性,这个共同的功能,可能是都遵循某个协议,或者都继承自某个类。利用这种设计模式,我们可以做出消息派发、遍历层级等多种功能。