先举一个简单的例子:比如现在有一个快递,但是这个快递可能是一个很大的包裹,在大的包裹里面可能还包含其他小包裹,小包裹里面可能是具体的物品:可能是一把牙刷,可能是一个杯子,也可能是一台电脑,也有可能是一个更小的包裹。如果现在想统计一共有多少把牙刷,把它映射到具体的软件系统中,就不得不变所有的包裹遍历一次,判断是不是牙刷,如果是包裹还要继续往里面遍历。因为整个系统里涉及的类型可能会很多,所以肯定会有很多if else 类型的判断,如果增加新的类型,就必须再增加一个if。维护起来是非常麻烦,最后可能会产生一个无比巨大的用来专门判断是某种类型的方法。
这个快递的例子是一个树状的结构,如果要优化解决需求的方法,那么它的核心是如何能更有效率的遍历节点。像这种情况使用组合模式就非常有效果了。
组合模式的UML图如下所示:
![](https://img.haomeiwen.com/i1460909/9610152ae6fa8b54.png)
组合模式将树形结构的节点(可能是"包裹",也可能是一个"具体的物品")抽象成了一个Component,这个Component有个execute方法用来处理所有节点共性的逻辑。Component的继承者中分了两种不同的类型,因为这两种类型也有显著不同的特点。一个是"具体的物品",通过Leaf来表示,它不能包含其他节点。另一种是Composite,它是一个集合类型,可能管理了不同的Leaf或者是更小的Composite。同时,Composite也可以被当做一个子节点被包含在其他Composite中。
在实际的例子中,Leaf可能也很多种:Leaf0,Leaf1,..., LeafN。每种Leaf的excute方法根据其特定的类型实现特定的不同逻辑。对于Composite的excute实现,其实是派发给它包含的所有Leaf进行处理了。所以对于使用者来说,Leaf和Componet本质上可能不需要区别对待的,因为它们都可以看作是Component。
总的来说,组合模式是比较好理解的。适合组合模式的场景是这种树状或者网状结构的业务模型,它提供了高效遍历节点的方法。
下面是一个使用组合模式的具体例子:能够画出各种形状的程序。
声明了抽象节点的接口,声明了一个id方法,是为了给每个节点设计唯一id,在集合处理删除元素会用到。
CompoentId的作用主要是为了统一处理id生成的逻辑,避免每个Node都需要自己生成。
protocol Component {
func move(point: CGPoint);
func draw();
func id() -> String;
}
class ComponentId: Component {
private let i: String
init() {
self.i = "\(Date().timeIntervalSince1970)"
}
func id() -> String {
return self.i
}
func move(point: CGPoint){
}
func draw(){
}
}
Leaf节点设计了Point和Circle两种类型
class Point: ComponentId {
var p: CGPoint = CGPoint(x: 0, y: 0)
override func move(point: CGPoint) {
self.p = point
}
override func draw() {
print("p = \(self.p), draw Point")
}
}
class Circle: ComponentId {
var p: CGPoint = CGPoint(x: 0, y: 0)
override func move(point: CGPoint) {
self.p = point
}
override func draw() {
print("p = \(self.p), draw Circle")
}
}
Composite的实现:移除元素需要通过元素自己定义的唯一id进行区分,通过id找到对应的元素
class Composite: ComponentId {
var children: [Component] = [Component]()
override func draw() {
for child in self.children {
child.draw()
}
}
override func move(point: CGPoint) {
for child in self.children {
child.move(point: point)
}
}
func addChild(c: Component) {
self.children.append(c)
}
func removeChild(c: Component) {
if let index = self.children.firstIndex(where: { (c2) -> Bool in
return c.id() == c2.id()
}) {
self.children.remove(at: index)
}
}
}
测试代码:
func compositeTest() {
let p = Point()
let circle = Circle()
let c = Composite()
c.addChild(c: p)
c.move(point: CGPoint(x: 100, y: 100))
c.draw()
c.addChild(c: circle)
c.move(point: CGPoint(x: 200, y: 200))
c.draw()
c.removeChild(c: p)
c.move(point: CGPoint(x: 150, y: 150))
c.draw()
}
网友评论