Skip to content

组合模式

约 702 字大约 2 分钟

设计模式

2018-05-05

什么是组合模式?

Composite(组合)模式 是一种结构型设计模式。

它用于将对象组织成树形结构,以表示 “部分-整体” 的层次关系。 它允许客户端以统一的方式处理单个对象和组合对象,忽略对象层次结构的差异。

组合模式主要由以下部分组成:

Component(抽象构件)

  • 定义所有对象的通用接口(包括叶子节点和容器)。
  • 声明操作子节点的方法(如 add、remove)。

Leaf(叶子节点)

  • 表示树中的末端对象(无子节点)。
  • 实现 Component 接口的基本行为。

Composite(容器节点)

  • 包含子节点(Leaf 或 Composite)。
  • 实现操作和管理子节点的方法。

实现组合模式

// 1. 抽象构件
class Component {
  constructor(name) {
    this.name = name
  }

  // 默认抛出错误(叶子节点需重写)
  add(component) {
    throw new Error('Leaf nodes cannot add children.')
  }

  remove(component) {
    throw new Error('Leaf nodes cannot remove children.')
  }

  // 核心方法(所有构件必须实现)
  operation(depth = 0) {
    throw new Error('Subclass must implement operation().')
  }
}

// 2. 叶子节点(无子节点)
class Leaf extends Component {
  operation(depth = 0) {
    console.log(`${' '.repeat(depth * 2)}Leaf: ${this.name}`)
  }
}

// 3. 容器节点(可包含子节点)
class Composite extends Component {
  constructor(name) {
    super(name)
    this.children = []
  }

  add(component) {
    this.children.push(component)
  }

  remove(component) {
    const index = this.children.indexOf(component)
    if (index !== -1)
      this.children.splice(index, 1)
  }

  operation(depth = 0) {
    console.log(`${' '.repeat(depth * 2)}Composite: ${this.name}`)
    // 递归调用子节点的 operation()
    this.children.forEach(child =>
      child.operation(depth + 1)
    )
  }
}

// 客户端使用
const root = new Composite('Root')
const branch1 = new Composite('Branch 1')
const branch2 = new Composite('Branch 2')

root.add(branch1)
root.add(branch2)

branch1.add(new Leaf('Leaf A'))
branch1.add(new Leaf('Leaf B'))
branch2.add(new Leaf('Leaf C'))

root.operation()
Composite: Root
  Composite: Branch 1
    Leaf: Leaf A
    Leaf: Leaf B
  Composite: Branch 2
    Leaf: Leaf C

优点

  • 统一处理:客户端无需区分叶子节点和容器节点。
  • 开闭原则:新增节点类型无需修改现有代码。
  • 简化复杂结构:通过递归机制处理树形结构,代码简洁。
  • 灵活性:动态组合对象,构建任意复杂度的结构。

缺点

  • 违反单一职责:Component 需同时管理叶子/容器逻辑(通过异常处理限制叶子节点)。
  • 过度泛化:所有构件使用相同接口,可能包含不适用方法(如叶子节点的 add())。
  • 类型检查问题:需在运行时检查节点类型(如避免在叶子上调用 add())。

适用场景

  • 树形结构处理

    • 文件系统(文件是叶子,文件夹是容器)。
    • 组织架构(员工是叶子,部门是容器)。
    • UI 组件库(按钮是叶子,面板是容器)。
  • 统一操作需求

    • 递归计算(如目录总大小)。
    • 批量执行(如禁用整个表单控件)。
  • 嵌套菜单/导航

    菜单项(叶子)和子菜单(容器)的统一渲染。