结构型模式-组合模式

组合模式

组合是一种结构型设计模式, 你可以使用它将对象组合成  树状结构,并且能像使用独立对象一样使用它们

  1. 组件(Component)接口描述了树中简单项目和复杂项目所 共有的操作
  2. 叶节点(Leaf)是树的基本结构,它不包含子项目。 一般情况下,叶节点最终会完成大部分的实际工作,因为它 们无法将工作指派给其他部分。
  3. 容器(Container)——又名“组合(Composite)”——是包含叶 节点或其他容器等子项目的单位。容器不知道其子项目所属 的具体类,它只通过通用的组件接口与其子项目交互。容器接收到请求后会将工作分配给自己的子项目,处理中间 结果,然后将最终结果返回给客户端
  4. 客户端(Client)通过组件接口与所有项目交互。因此,客 户端能以相同方式与树状结构中的简单或复杂项目交互

Example

组件接口

package main

type Component interface {
search(string)
}

组合

package main

import "fmt"

type Folder struct {
components []Component
name string
}

func (f *Folder) search(keyword string) {
fmt.Printf("Serching recursively for keyword %s in folder %s\n", keyword, f.name)
for _, composite := range f.components {
composite.search(keyword)
}
}

func (f *Folder) add(c Component) {
f.components = append(f.components, c)
}

叶子

package main

import "fmt"

type File struct {
name string
}

func (f *File) search(keyword string) {
fmt.Printf("Searching for keyword %s in file %s\n", keyword, f.name)
}

func (f *File) getName() string {
return f.name
}

客户端

package main

func main() {
file1 := &File{name: "File1"}
file2 := &File{name: "File2"}
file3 := &File{name: "File3"}

folder1 := &Folder{
name: "Folder1",
}

folder1.add(file1)

folder2 := &Folder{
name: "Folder2",
}
folder2.add(file2)
folder2.add(file3)
folder2.add(folder1)

folder2.search("rose")
}

适用场景

  1. 如果你需要实现树状对象结构,可以使用组合模式
  • 组合模式为你提供了两种共享公共接口的基本元素类型:简 单叶节点和复杂容器。 容器中可以包含叶节点和其他容器。 这使得你可以构建树状嵌套递归对象结构
  1. 如果你希望客户端代码以相同方式处理简单和复杂元素,可 以使用该模式。
  • 组合模式中定义的所有元素共用同一个接口。在这一接口的 帮助下,客户端不必在意其所使用的对象的具体类

优缺点

优点

  • 利用多态和递归机制更方便地使用复杂树结构
  • 开闭原则。无需更改现有代码,你就可以在应用中添加新元 素,使其成为对象树的一部分

缺点

  • 对于功能差异较大的类,提供公共接口或许会有困难。在特 定情况下,你需要过度一般化组件接口,使其变得令人难以理解

与其他模式的关系

  • 桥接、状态和策略(在某种程度上包括适配器)模式的接口 非常相似。实际上,它们都基于组合模式——即将工作委派 给其他对象,不过也各自解决了不同的问题。模式并不只是 以特定方式组织代码的配方,你还可以使用它们来和其他开 发者讨论模式所解决的问题
  • 你可以在创建复杂组合树时使用生成器,因为这可使其构造 步骤以递归的方式运行
  • 责任链通常和组合模式结合使用。在这种情况下,叶组件接 收到请求后,可以将请求沿包含全体父组件的链一直传递至 对象树的底部
  • 可以使用迭代器来遍历组合树。
  • 你可以使用访问者对整个组合树执行操作
  • 你可以使用享元实现组合树的共享叶节点以节省内存。
  • 组合和装饰的结构图很相似,因为两者都依赖递归组合来组 织无限数量的对象
  • 大量使用组合和装饰的设计通常可从对于原型的使用中获益。 你可以通过该模式来复制复杂结构,而非从零开始重新构造