Skip to content

组合设计模式 / Composite

组合设计模式允许我们将对象组合成树形结构来标识“部分-整体”的层次结构。这种模式使得代码可以一致地处理单个对象和对象组合。

基于 Class 的代码示例

javascript
class Employee {
  constructor(id, name) {
    // id 属性用于在删除时进行查找
    this.id = id;
    this.name = name;
  }
  print() {
    console.log(`Employee: ${this.name}`);
  }
}

class Manager extends Employee {
  constructor(id, name) {
    super(id, name);
    this.employees = [];
  }
  add(employee) {
    this.employees.push(employee);
  }
  remove(id) {
    const index = this.employees.findIndex((item) => item.id === id);
    if (index >= 0) {
      this.employees.splice(index, 1);
    }
  }
  print() {
    console.log(`Manager: ${this.name}`);
    this.employees.forEach((employee) => employee.print());
  }
}

const ming = new Employee(1, 'Ming');
const anny = new Employee(2, 'Anny');

const bob = new Manager(3, 'Bob');

bob.add(ming);
bob.add(anny);

bob.print();

在上述代码中,Manager 对象实例可以添加和删除 Employee 实例,并且拥有自己的 print() 方法,该方法还会调用所有 Employee 实例的 print() 方法。

React 函数式组件代码示例

javascript
import React from 'react';

// 共享接口 (这里我们不需要共享接口,因为React的组件本身就是函数)
// class Component {
//   render() {
//     return null;
//   }
// }

// 叶子节点
const Post = ({ title, content, comments }) => (
  <div className="post">
    <h2>{title}</h2>
    <p>{content}</p>
    {comments.map((comment, index) => (
      <Comment key={index} author={comment.author} text={comment.text} />
    ))}
  </div>
);

// 叶子节点
const Comment = ({ author, text }) => (
  <div className="comment">
    <span>{author}:</span>
    <p>{text}</p>
  </div>
);

// 组合节点
const BlogPost = ({ post, comments }) => <Post title={post.title} content={post.content} comments={comments} />;

// 使用
const post = {
  title: 'My First Post',
  content: 'Welcome to my blog!',
};
const comments = [
  { author: 'Alice', text: 'Great post!' },
  { author: 'Bob', text: 'Thanks for sharing.' },
];

ReactDOM.render(<BlogPost post={post} comments={comments} />, document.getElementById('root'));

这样做的好处:

  1. 灵活性:由于客户端代码与组合结构的交互方式是统一的,因此,你可以轻松地添加新的组件类型或改变现有组件的行为,而不会影响到客户端代码。例如,如果你需要添加一个新的评论类型,如“匿名评论”,只需要创建一个新的组件类并遵循相同的接口,而无需修改已有的 CommentPost 类。

  2. 可扩展性:通过组合,你可以轻松地增加或减少组件的层次,添加更多的功能。例如,如果博客应用需要添加“点赞”功能,你可以创建一个 LikeButton 组件,并将其添加到 PostComment 中,而无需更改整体结构。

  3. 代码复用:组合模式允许你重用单个组件,因为它们可以独立于组合存在。例如,Comment 组件不仅可以用于博客,还可以用于论坛、社交媒体等其他场景。

  4. 清晰的结构:组合模式提供了一种清晰的方式来表示对象之间的部分-整体关系,使得代码更易于理解和维护。当需求变化时,你可以更容易地追踪和修改这些关系。

例如,假设你的博客应用现在要求用户可以对评论进行回复。你可以创建一个新的 Reply 组件,它同样遵循 Component 接口,然后在 Comment 组件中添加一个 replies 属性,并修改 Commentrender 方法以显示回复。由于所有组件都遵循相同的接口,你不需要修改 PostBlogPost 的代码,只需关注 Comment 和新的 Reply 组件即可。这就是组合设计模式在需求变更时的优势。