Skip to content

组合模式详解:概念、实现与应用

引言

组合模式是一种结构型设计模式,它允许你将对象组合成树形结构来表示"部分-整体"的层次关系。组合模式使得客户端对单个对象和组合对象的使用具有一致性。

什么是组合模式?

组合模式是一种结构型设计模式,它允许你将对象组合成树形结构来表示"部分-整体"的层次关系。组合模式使得客户端对单个对象和组合对象的使用具有一致性。

核心思想

组合模式的核心思想是:

  1. 统一接口:为叶子节点和组合节点提供统一的接口
  2. 递归结构:支持对象的递归组合
  3. 透明性:客户端无需区分单个对象和组合对象

为什么需要组合模式?

在许多情况下,我们需要处理具有层次结构的对象:

1. 树形结构

当需要表示对象的层次结构时:

  • 文件系统(文件和文件夹)
  • 组织架构(员工和部门)
  • 图形界面(组件和容器)

2. 统一处理

当需要统一处理单个对象和对象组合时:

  • 避免类型检查
  • 简化客户端代码
  • 提高代码复用性

3. 递归操作

当需要对整个结构进行递归操作时:

  • 计算总和或统计
  • 遍历所有节点
  • 应用统一的操作

组合模式的基本实现

让我们从一个简单的组合模式实现开始:

javascript
// 组件接口
class Component {
  constructor(name) {
    this.name = name;
  }
  
  operation() {
    throw new Error('必须实现 operation 方法');
  }
  
  add(component) {
    throw new Error('不支持添加操作');
  }
  
  remove(component) {
    throw new Error('不支持删除操作');
  }
  
  getChild(index) {
    throw new Error('不支持获取子节点操作');
  }
}

// 叶子节点
class Leaf extends Component {
  operation() {
    return `叶子节点 ${this.name} 执行操作`;
  }
}

// 组合节点
class Composite extends Component {
  constructor(name) {
    super(name);
    this.children = [];
  }
  
  operation() {
    let result = `组合节点 ${this.name} 执行操作\n`;
    for (const child of this.children) {
      result += `  ${child.operation()}\n`;
    }
    return result;
  }
  
  add(component) {
    this.children.push(component);
  }
  
  remove(component) {
    const index = this.children.indexOf(component);
    if (index !== -1) {
      this.children.splice(index, 1);
    }
  }
  
  getChild(index) {
    return this.children[index];
  }
}

// 客户端代码
function clientCode(component) {
  console.log(component.operation());
}

// 使用组合模式
const root = new Composite('根节点');
const branch1 = new Composite('分支1');
const branch2 = new Composite('分支2');
const leaf1 = new Leaf('叶子1');
const leaf2 = new Leaf('叶子2');
const leaf3 = new Leaf('叶子3');

branch1.add(leaf1);
branch1.add(leaf2);
branch2.add(leaf3);
root.add(branch1);
root.add(branch2);

clientCode(root);

实现要点分析

  1. 组件接口:定义所有组件的公共接口
  2. 叶子节点:表示树形结构的末端节点
  3. 组合节点:包含子节点的节点,可以继续添加子节点
  4. 客户端:通过统一接口操作所有节点
  5. 递归结构:支持任意层次的嵌套

组合模式的实际应用场景

1. 文件系统

在文件系统中,组合模式非常适合用于表示文件和文件夹的层次结构:

javascript
// 文件系统组件接口
class FileSystemComponent {
  constructor(name) {
    this.name = name;
  }
  
  getSize() {
    throw new Error('必须实现 getSize 方法');
  }
  
  getName() {
    return this.name;
  }
  
  getPath() {
    throw new Error('必须实现 getPath 方法');
  }
  
  add(component) {
    throw new Error('不支持添加操作');
  }
  
  remove(component) {
    throw new Error('不支持删除操作');
  }
}

// 文件(叶子节点)
class File extends FileSystemComponent {
  constructor(name, size) {
    super(name);
    this.size = size;
  }
  
  getSize() {
    return this.size;
  }
  
  getPath() {
    return `/${this.name}`;
  }
  
  getInfo() {
    return `文件: ${this.name}, 大小: ${this.size}KB`;
  }
}

// 文件夹(组合节点)
class Folder extends FileSystemComponent {
  constructor(name) {
    super(name);
    this.children = [];
    this.parent = null;
  }
  
  getSize() {
    return this.children.reduce((total, child) => total + child.getSize(), 0);
  }
  
  getPath() {
    if (this.parent) {
      return `${this.parent.getPath()}/${this.name}`;
    }
    return `/${this.name}`;
  }
  
  add(component) {
    this.children.push(component);
    component.parent = this;
  }
  
  remove(component) {
    const index = this.children.indexOf(component);
    if (index !== -1) {
      this.children.splice(index, 1);
      component.parent = null;
    }
  }
  
  getChild(index) {
    return this.children[index];
  }
  
  getChildren() {
    return [...this.children];
  }
  
  getInfo() {
    return `文件夹: ${this.name}, 大小: ${this.getSize()}KB, 包含 ${this.children.length} 个项目`;
  }
  
  listContents(indent = 0) {
    const indentStr = '  '.repeat(indent);
    let result = `${indentStr}${this.getInfo()}\n`;
    
    for (const child of this.children) {
      if (child instanceof Folder) {
        result += child.listContents(indent + 1);
      } else {
        result += `${indentStr}  ${child.getInfo()}\n`;
      }
    }
    
    return result;
  }
  
  search(name) {
    const results = [];
    
    // 检查当前节点
    if (this.name === name) {
      results.push(this);
    }
    
    // 递归搜索子节点
    for (const child of this.children) {
      if (child.getName() === name) {
        results.push(child);
      }
      
      if (child instanceof Folder) {
        results.push(...child.search(name));
      }
    }
    
    return results;
  }
}

// 文件系统管理器
class FileSystemManager {
  constructor() {
    this.root = new Folder('根目录');
  }
  
  createFile(path, size) {
    const parts = path.split('/').filter(part => part !== '');
    const filename = parts.pop();
    let current = this.root;
    
    // 导航到目标文件夹
    for (const part of parts) {
      let folder = current.getChildren().find(child => child.getName() === part && child instanceof Folder);
      if (!folder) {
        folder = new Folder(part);
        current.add(folder);
      }
      current = folder;
    }
    
    // 创建文件
    const file = new File(filename, size);
    current.add(file);
    return file;
  }
  
  createFolder(path) {
    const parts = path.split('/').filter(part => part !== '');
    let current = this.root;
    
    // 创建文件夹层次结构
    for (const part of parts) {
      let folder = current.getChildren().find(child => child.getName() === part && child instanceof Folder);
      if (!folder) {
        folder = new Folder(part);
        current.add(folder);
      }
      current = folder;
    }
    
    return current;
  }
  
  listDirectory(path = '/') {
    if (path === '/') {
      return this.root.listContents();
    }
    
    // 查找指定路径的文件夹
    const parts = path.split('/').filter(part => part !== '');
    let current = this.root;
    
    for (const part of parts) {
      const folder = current.getChildren().find(child => child.getName() === part && child instanceof Folder);
      if (!folder) {
        throw new Error(`路径不存在: ${path}`);
      }
      current = folder;
    }
    
    return current.listContents();
  }
  
  getTotalSize() {
    return this.root.getSize();
  }
  
  search(name) {
    return this.root.search(name);
  }
}

// 使用示例
function fileSystemExample() {
  const fs = new FileSystemManager();
  
  console.log('=== 创建文件系统结构 ===');
  
  // 创建文件夹
  fs.createFolder('文档/工作');
  fs.createFolder('文档/个人');
  fs.createFolder('图片/风景');
  fs.createFolder('图片/人物');
  fs.createFolder('音乐/流行');
  fs.createFolder('音乐/古典');
  
  // 创建文件
  fs.createFile('文档/工作/报告.docx', 1024);
  fs.createFile('文档/工作/计划.xlsx', 512);
  fs.createFile('文档/个人/日记.txt', 256);
  fs.createFile('图片/风景/海滩.jpg', 2048);
  fs.createFile('图片/风景/山脉.jpg', 3072);
  fs.createFile('图片/人物/肖像.jpg', 1536);
  fs.createFile('音乐/流行/歌曲1.mp3', 4096);
  fs.createFile('音乐/古典/交响乐.mp3', 8192);
  
  console.log('\n=== 显示文件系统结构 ===');
  console.log(fs.listDirectory());
  
  console.log('\n=== 显示特定目录 ===');
  console.log(fs.listDirectory('/图片'));
  
  console.log('\n=== 文件系统总大小 ===');
  console.log(`总大小: ${fs.getTotalSize()}KB`);
  
  console.log('\n=== 搜索文件 ===');
  const results = fs.search('报告.docx');
  console.log('搜索结果:');
  results.forEach(result => console.log(`  ${result.getPath()}`));
}

fileSystemExample();

2. 图形用户界面

在GUI系统中,组合模式非常适合用于表示界面组件的层次结构:

javascript
// GUI组件接口
class GUIComponent {
  constructor(name) {
    this.name = name;
    this.parent = null;
    this.x = 0;
    this.y = 0;
  }
  
  render() {
    throw new Error('必须实现 render 方法');
  }
  
  setPosition(x, y) {
    this.x = x;
    this.y = y;
  }
  
  getPosition() {
    return { x: this.x, y: this.y };
  }
  
  add(component) {
    throw new Error('不支持添加操作');
  }
  
  remove(component) {
    throw new Error('不支持删除操作');
  }
  
  getChild(index) {
    throw new Error('不支持获取子节点操作');
  }
  
  handleEvent(event) {
    // 默认事件处理
    if (this.parent) {
      this.parent.handleEvent(event);
    }
  }
}

// 基础组件(叶子节点)
class BasicComponent extends GUIComponent {
  constructor(name, type) {
    super(name);
    this.type = type;
    this.visible = true;
    this.enabled = true;
  }
  
  render() {
    if (!this.visible) return '';
    return `${this.type} "${this.name}" at (${this.x}, ${this.y})`;
  }
  
  setVisible(visible) {
    this.visible = visible;
  }
  
  setEnabled(enabled) {
    this.enabled = enabled;
  }
  
  handleClick() {
    if (this.enabled) {
      console.log(`点击了 ${this.type} "${this.name}"`);
      this.handleEvent({ type: 'click', target: this });
    }
  }
}

// 按钮组件
class Button extends BasicComponent {
  constructor(name, text) {
    super(name, 'Button');
    this.text = text;
  }
  
  render() {
    if (!this.visible) return '';
    const state = this.enabled ? '' : ' [disabled]';
    return `Button "${this.text}" at (${this.x}, ${this.y})${state}`;
  }
}

// 文本框组件
class TextBox extends BasicComponent {
  constructor(name, placeholder = '') {
    super(name, 'TextBox');
    this.placeholder = placeholder;
    this.value = '';
  }
  
  render() {
    if (!this.visible) return '';
    const state = this.enabled ? '' : ' [disabled]';
    return `TextBox "${this.placeholder}" at (${this.x}, ${this.y})${state}`;
  }
  
  setValue(value) {
    if (this.enabled) {
      this.value = value;
    }
  }
}

// 标签组件
class Label extends BasicComponent {
  constructor(name, text) {
    super(name, 'Label');
    this.text = text;
  }
  
  render() {
    if (!this.visible) return '';
    return `Label "${this.text}" at (${this.x}, ${this.y})`;
  }
}

// 容器组件(组合节点)
class Container extends GUIComponent {
  constructor(name) {
    super(name);
    this.children = [];
    this.visible = true;
  }
  
  render() {
    if (!this.visible) return '';
    
    let result = `Container "${this.name}" at (${this.x}, ${this.y}):\n`;
    for (const child of this.children) {
      const childRender = child.render();
      if (childRender) {
        result += `  ${childRender}\n`;
      }
    }
    return result;
  }
  
  add(component) {
    this.children.push(component);
    component.parent = this;
  }
  
  remove(component) {
    const index = this.children.indexOf(component);
    if (index !== -1) {
      this.children.splice(index, 1);
      component.parent = null;
    }
  }
  
  getChild(index) {
    return this.children[index];
  }
  
  getChildren() {
    return [...this.children];
  }
  
  setVisible(visible) {
    this.visible = visible;
    // 递归设置子组件的可见性
    for (const child of this.children) {
      if (child.setVisible) {
        child.setVisible(visible);
      }
    }
  }
  
  handleEvent(event) {
    // 容器可以处理事件
    console.log(`容器 "${this.name}" 处理事件: ${event.type}`);
    
    // 传递给父容器
    super.handleEvent(event);
  }
  
  findComponent(name) {
    if (this.name === name) {
      return this;
    }
    
    for (const child of this.children) {
      if (child.name === name) {
        return child;
      }
      
      if (child instanceof Container) {
        const found = child.findComponent(name);
        if (found) {
          return found;
        }
      }
    }
    
    return null;
  }
}

// 窗口容器
class Window extends Container {
  constructor(name, width, height) {
    super(name);
    this.width = width;
    this.height = height;
  }
  
  render() {
    if (!this.visible) return '';
    
    let result = `Window "${this.name}" (${this.width}x${this.height}) at (${this.x}, ${this.y}):\n`;
    for (const child of this.children) {
      const childRender = child.render();
      if (childRender) {
        result += `  ${childRender}\n`;
      }
    }
    return result;
  }
}

// 面板容器
class Panel extends Container {
  constructor(name) {
    super(name);
  }
  
  render() {
    if (!this.visible) return '';
    
    let result = `Panel "${this.name}" at (${this.x}, ${this.y}):\n`;
    for (const child of this.children) {
      const childRender = child.render();
      if (childRender) {
        result += `    ${childRender}\n`;
      }
    }
    return result;
  }
}

// GUI应用程序
class GUIApplication {
  constructor() {
    this.windows = [];
  }
  
  addWindow(window) {
    this.windows.push(window);
  }
  
  render() {
    let result = '=== GUI 应用程序 ===\n';
    for (const window of this.windows) {
      result += window.render() + '\n';
    }
    return result;
  }
  
  findComponent(name) {
    for (const window of this.windows) {
      const found = window.findComponent(name);
      if (found) {
        return found;
      }
    }
    return null;
  }
  
  simulateClick(componentName) {
    const component = this.findComponent(componentName);
    if (component && component.handleClick) {
      component.handleClick();
    } else {
      console.log(`未找到组件: ${componentName}`);
    }
  }
}

// 使用示例
function guiExample() {
  const app = new GUIApplication();
  
  console.log('=== 创建GUI界面 ===');
  
  // 创建主窗口
  const mainWindow = new Window('主窗口', 800, 600);
  mainWindow.setPosition(100, 100);
  
  // 创建菜单栏
  const menuBar = new Panel('菜单栏');
  menuBar.setPosition(0, 0);
  
  const fileMenu = new Button('文件菜单', '文件');
  fileMenu.setPosition(0, 0);
  
  const editMenu = new Button('编辑菜单', '编辑');
  editMenu.setPosition(60, 0);
  
  menuBar.add(fileMenu);
  menuBar.add(editMenu);
  
  // 创建工具栏
  const toolbar = new Panel('工具栏');
  toolbar.setPosition(0, 30);
  
  const newButton = new Button('新建按钮', '新建');
  newButton.setPosition(0, 0);
  
  const openButton = new Button('打开按钮', '打开');
  openButton.setPosition(60, 0);
  
  const saveButton = new Button('保存按钮', '保存');
  saveButton.setPosition(120, 0);
  
  toolbar.add(newButton);
  toolbar.add(openButton);
  toolbar.add(saveButton);
  
  // 创建主内容区域
  const contentPanel = new Panel('内容面板');
  contentPanel.setPosition(0, 60);
  
  const titleLabel = new Label('标题标签', '欢迎使用我们的应用程序');
  titleLabel.setPosition(10, 10);
  
  const nameTextBox = new TextBox('姓名输入框', '请输入姓名');
  nameTextBox.setPosition(10, 40);
  
  const emailTextBox = new TextBox('邮箱输入框', '请输入邮箱');
  emailTextBox.setPosition(10, 70);
  
  const submitButton = new Button('提交按钮', '提交');
  submitButton.setPosition(10, 100);
  
  contentPanel.add(titleLabel);
  contentPanel.add(nameTextBox);
  contentPanel.add(emailTextBox);
  contentPanel.add(submitButton);
  
  // 组装界面
  mainWindow.add(menuBar);
  mainWindow.add(toolbar);
  mainWindow.add(contentPanel);
  
  app.addWindow(mainWindow);
  
  console.log('\n=== 渲染界面 ===');
  console.log(app.render());
  
  console.log('\n=== 模拟用户交互 ===');
  app.simulateClick('提交按钮');
  app.simulateClick('文件菜单');
  app.simulateClick('不存在的按钮');
  
  console.log('\n=== 查找并操作组件 ===');
  const foundComponent = app.findComponent('邮箱输入框');
  if (foundComponent) {
    console.log(`找到组件: ${foundComponent.render()}`);
    if (foundComponent.setValue) {
      foundComponent.setValue('user@example.com');
      console.log('已设置邮箱值');
    }
  }
  
  console.log('\n=== 隐藏面板 ===');
  contentPanel.setVisible(false);
  console.log('隐藏内容面板后:');
  console.log(app.render());
}

guiExample();

3. 组织架构管理

在企业管理系统中,组合模式非常适合用于表示组织架构:

javascript
// 组织成员接口
class OrganizationMember {
  constructor(name, position) {
    this.name = name;
    this.position = position;
  }
  
  getDetails() {
    throw new Error('必须实现 getDetails 方法');
  }
  
  getSalary() {
    throw new Error('必须实现 getSalary 方法');
  }
  
  getTotalSalary() {
    throw new Error('必须实现 getTotalSalary 方法');
  }
  
  add(member) {
    throw new Error('不支持添加操作');
  }
  
  remove(member) {
    throw new Error('不支持删除操作');
  }
  
  getSubordinates() {
    return [];
  }
}

// 员工(叶子节点)
class Employee extends OrganizationMember {
  constructor(name, position, salary) {
    super(name, position);
    this.salary = salary;
  }
  
  getDetails() {
    return `${this.position}: ${this.name} (¥${this.salary})`;
  }
  
  getSalary() {
    return this.salary;
  }
  
  getTotalSalary() {
    return this.salary;
  }
}

// 管理者(组合节点)
class Manager extends OrganizationMember {
  constructor(name, position, salary) {
    super(name, position);
    this.salary = salary;
    this.subordinates = [];
  }
  
  getDetails() {
    return `${this.position}: ${this.name} (¥${this.salary}) 管理 ${this.subordinates.length} 名下属`;
  }
  
  getSalary() {
    return this.salary;
  }
  
  getTotalSalary() {
    let total = this.salary;
    for (const subordinate of this.subordinates) {
      total += subordinate.getTotalSalary();
    }
    return total;
  }
  
  add(member) {
    this.subordinates.push(member);
  }
  
  remove(member) {
    const index = this.subordinates.indexOf(member);
    if (index !== -1) {
      this.subordinates.splice(index, 1);
    }
  }
  
  getSubordinates() {
    return [...this.subordinates];
  }
  
  getOrganizationStructure(level = 0) {
    const indent = '  '.repeat(level);
    let result = `${indent}${this.getDetails()}\n`;
    
    for (const subordinate of this.subordinates) {
      if (subordinate instanceof Manager) {
        result += subordinate.getOrganizationStructure(level + 1);
      } else {
        result += `${indent}  ${subordinate.getDetails()}\n`;
      }
    }
    
    return result;
  }
  
  findMember(name) {
    if (this.name === name) {
      return this;
    }
    
    for (const subordinate of this.subordinates) {
      if (subordinate.name === name) {
        return subordinate;
      }
      
      if (subordinate instanceof Manager) {
        const found = subordinate.findMember(name);
        if (found) {
          return found;
        }
      }
    }
    
    return null;
  }
  
  getTeamMembers() {
    const members = [this];
    
    for (const subordinate of this.subordinates) {
      if (subordinate instanceof Manager) {
        members.push(...subordinate.getTeamMembers());
      } else {
        members.push(subordinate);
      }
    }
    
    return members;
  }
}

// 组织架构管理器
class OrganizationManager {
  constructor(companyName) {
    this.companyName = companyName;
    this.ceo = null;
  }
  
  setCEO(ceo) {
    this.ceo = ceo;
  }
  
  getOrganizationStructure() {
    if (!this.ceo) {
      return '尚未设置CEO';
    }
    
    return `公司: ${this.companyName}\n` + this.ceo.getOrganizationStructure();
  }
  
  getTotalSalary() {
    if (!this.ceo) {
      return 0;
    }
    
    return this.ceo.getTotalSalary();
  }
  
  findMember(name) {
    if (!this.ceo) {
      return null;
    }
    
    return this.ceo.findMember(name);
  }
  
  getTeamMembers(managerName) {
    const manager = this.findMember(managerName);
    if (manager instanceof Manager) {
      return manager.getTeamMembers();
    }
    return [];
  }
  
  getManagers() {
    const managers = [];
    
    if (this.ceo instanceof Manager) {
      const traverse = (member) => {
        if (member instanceof Manager) {
          managers.push(member);
          for (const subordinate of member.getSubordinates()) {
            traverse(subordinate);
          }
        }
      };
      
      traverse(this.ceo);
    }
    
    return managers;
  }
}

// 使用示例
function organizationExample() {
  const org = new OrganizationManager('科技有限公司');
  
  console.log('=== 创建组织架构 ===');
  
  // 创建CEO
  const ceo = new Manager('张三', 'CEO', 100000);
  
  // 创建部门经理
  const techManager = new Manager('李四', '技术部经理', 80000);
  const salesManager = new Manager('王五', '销售部经理', 75000);
  const hrManager = new Manager('赵六', '人事部经理', 70000);
  
  // 创建技术部员工
  const developer1 = new Employee('小明', '高级开发工程师', 60000);
  const developer2 = new Employee('小红', '中级开发工程师', 50000);
  const tester = new Employee('小刚', '测试工程师', 45000);
  
  // 创建销售部员工
  const sales1 = new Employee('小李', '高级销售', 55000);
  const sales2 = new Employee('小王', '中级销售', 45000);
  
  // 创建人事部员工
  const hrSpecialist = new Employee('小芳', '人事专员', 40000);
  
  // 构建组织架构
  techManager.add(developer1);
  techManager.add(developer2);
  techManager.add(tester);
  
  salesManager.add(sales1);
  salesManager.add(sales2);
  
  hrManager.add(hrSpecialist);
  
  ceo.add(techManager);
  ceo.add(salesManager);
  ceo.add(hrManager);
  
  org.setCEO(ceo);
  
  console.log('\n=== 显示组织架构 ===');
  console.log(org.getOrganizationStructure());
  
  console.log('\n=== 薪资统计 ===');
  console.log(`公司总薪资支出: ¥${org.getTotalSalary()}`);
  
  console.log('\n=== 查找员工 ===');
  const foundMember = org.findMember('小红');
  if (foundMember) {
    console.log(`找到员工: ${foundMember.getDetails()}`);
  }
  
  console.log('\n=== 团队成员 ===');
  const techTeam = org.getTeamMembers('李四');
  console.log('技术部团队成员:');
  techTeam.forEach(member => console.log(`  ${member.getDetails()}`));
  
  console.log('\n=== 管理层列表 ===');
  const managers = org.getManagers();
  console.log('所有管理者:');
  managers.forEach(manager => console.log(`  ${manager.getDetails()}`));
}

organizationExample();

组合模式的两种实现方式

1. 透明方式

在透明方式中,Component接口声明了所有管理子对象的方法:

javascript
// 透明方式 - 所有方法都在接口中声明
class Component {
  add(component) {
    // 默认实现:不支持添加
    throw new Error('不支持添加操作');
  }
  
  remove(component) {
    // 默认实现:不支持删除
    throw new Error('不支持删除操作');
  }
  
  getChild(index) {
    // 默认实现:不支持获取子节点
    throw new Error('不支持获取子节点操作');
  }
}

2. 安全方式

在安全方式中,只在Composite类中声明管理子对象的方法:

javascript
// 安全方式 - 只在组合节点中声明管理方法
class Component {
  // 不声明管理子对象的方法
}

class Composite extends Component {
  add(component) {
    // 只在组合节点中实现
  }
  
  remove(component) {
    // 只在组合节点中实现
  }
  
  getChild(index) {
    // 只在组合节点中实现
  }
}

组合模式与其它模式的对比

组合模式 vs 装饰器模式

javascript
// 组合模式 - 表示"部分-整体"层次结构
class Composite {
  add(component) {
    this.children.push(component);
  }
}

// 装饰器模式 - 动态添加职责
class Decorator {
  constructor(component) {
    this.component = component;
  }
  
  operation() {
    // 增强原有功能
    return this.component.operation();
  }
}

组合模式 vs 迭代器模式

javascript
// 组合模式 - 组织对象的层次结构
class Composite {
  getChildren() {
    return this.children;
  }
}

// 迭代器模式 - 提供遍历元素的方法
class Iterator {
  hasNext() {
    // 判断是否还有下一个元素
  }
  
  next() {
    // 获取下一个元素
  }
}

组合模式的优缺点

优点

  1. 统一接口:客户端可以一致地使用单个对象和组合对象
  2. 简化客户端:无需区分叶子节点和组合节点
  3. 增加灵活性:可以很容易地增加新的组件类型
  4. 递归遍历:天然支持递归操作和遍历
  5. 符合开闭原则:添加新组件无需修改现有代码

缺点

  1. 设计复杂:需要正确识别层次结构
  2. 类型安全:难以限制容器中的构件类型
  3. 性能开销:递归操作可能带来性能问题
  4. 过度泛化:可能被过度使用

总结

组合模式是一种结构型设计模式,它允许你将对象组合成树形结构来表示"部分-整体"的层次关系。组合模式使得客户端对单个对象和组合对象的使用具有一致性。

通过本章的学习,我们了解了:

  1. 组合模式的基本概念和核心思想
  2. 组合模式的两种实现方式(透明方式和安全方式)
  3. 组合模式在实际开发中的应用场景(文件系统、GUI界面、组织架构)
  4. 组合模式与其他结构型模式的对比
  5. 组合模式的优缺点

组合模式在现代软件开发中应用广泛,特别是在需要表示对象层次结构、统一处理单个对象和对象组合的场景中,它可以很好地支持系统的灵活性和可维护性。

在下一章中,我们将继续探讨其他结构型模式,接下来是享元模式。