前端设计模式之组合模式
·
问题画像
- tree 结构;
- 主要分为
组合节点与叶子节点; 组合节点存在一个children字段,用于保存其他组合节点与叶子节点;- 用户可以使用一套
api去操作这两个节点。
比如: 文件系统和公司的组织架构图;对于文件系统来说,目录和文件,这两者整体可以使用一套 api,方便用户使用和理解,而对于其中的差异,可以单独处理。
定义
组合模式允许以相同的方式处理单个对象和复合对象,意图是将对象组合成树形结构以表示 “部分 - 整体” 的层次结构;
组合模式 的核心是简单对象和复合对象必须实现相同的接口。这就是组合模式能够将组合对象和简单对象进行一致处理的原因。
总体结构:
- 一个
抽象类,也是暴露给外界的 api; 单个对象,也就是叶子(Leaf)节点,需要继承抽象类;复合对象,也就是容器组件(Composite),需要继承抽象类,区别于叶子节点的是,存在一个children属性,用于保存叶子节点以及其他容器组件;
实现
当前场景是一个简单的文件系统,属于树形结构,实现了添加文件到目录,已经获取当前目录下有多少文件的功能。
abstract class FileComponent {
isFile: boolean;
fileName: string;
constructor(fileName: string, isFile: boolean) {
this.isFile = isFile;
this.fileName = fileName;
}
abstract add(file: FileLeaf | Folder): FileComponent;
abstract getFileCount(): number;
}
// 组合节点
class Folder extends FileComponent {
// 这里是比较核心的点,树结构用来
list: FileLeaf[] = [];
constructor(fileName: string, isFile: boolean) {
super(fileName, isFile);
}
add(file: FileLeaf): FileComponent {
const result = this.list.some((item) => item.fileName === file.fileName);
if (result) return this;
this.list.push(file);
return this;
}
getFileCount() {
const fileCount = this.list.reduce((accumulator, item) => {
if (item.isFile) return accumulator + 1;
return accumulator;
}, 0);
return fileCount;
}
}
// 叶子节点
class FileLeaf extends FileComponent {
constructor(fileName: string, isFile: boolean) {
super(fileName, isFile);
}
add(): FileLeaf {
throw new Error("文件不可执行该操作。");
}
getFileCount(): number {
return 1;
}
}
// 创建一个根目录
const root = new Folder('root', false);
// 创建两个目录
const folder = new Folder('folder', false);
// 创建三个文件
const file1 = new FileLeaf('file1', true);
const file2 = new FileLeaf('file2', true);
const file3 = new FileLeaf('file3', true);
folder.add(file3);
root.add(file1)
.add(file2)
.add(folder);
console.log(root.getFileCount()); // 2
console.log(folder.getFileCount()); // 1
总结
- 主要优点有:
- 客户端可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端操作;
- 更容易组合整个组合体,不需要修改源代码;
- 其主要缺点是:
- 设计较复杂,客户端需要花更多时间理清类之间的层次关系;
- 不容易处理容器中比较特殊的点,缺少普遍性。
更多推荐



所有评论(0)