8. 组合模式(Composite)
8. 组合模式(Composite)
问题:想统一对待单个对象和组合对象(如文件与文件夹)。
核心:定义统一接口,叶子和容器都实现它,容器递归调用子元素。
interface TreeNode {
display(indent?: string): void;
}
class FileNode implements TreeNode {
constructor(private name: string) {}
display(indent = ''): void { console.log(`${indent}- ${this.name}`); }
}
class FolderNode implements TreeNode {
private children: TreeNode[] = [];
constructor(private name: string) {}
add(child: TreeNode): this { this.children.push(child); return this; }
display(indent = ''): void {
console.log(`${indent}📁 ${this.name}`);
this.children.forEach(c => c.display(indent + ' '));
}
}
const root = new FolderNode('项目')
.add(new FileNode('index.ts'))
.add(new FolderNode('src').add(new FileNode('app.ts')).add(new FileNode('utils.ts')));
root.display();
// 📁 项目
// - index.ts
// 📁 src
// - app.ts
// - utils.tspackage composite
import "fmt"
type Component interface {
Display(indent string)
}
type File struct{ Name string }
func (f File) Display(indent string) {
fmt.Printf("%s- %s\n", indent, f.Name)
}
type Folder struct {
Name string
Children []Component
}
func (f *Folder) Add(c Component) *Folder {
f.Children = append(f.Children, c)
return f
}
func (f *Folder) Display(indent string) {
fmt.Printf("%s📁 %s\n", indent, f.Name)
for _, child := range f.Children {
child.Display(indent + " ")
}
}实战场景
- 文件系统:文件和文件夹都能展示、删除、计算大小,文件夹只是递归处理子节点。
- 前端组件树:页面、布局、组件、文本节点都可以抽象成节点,渲染时递归遍历整棵树。
- 菜单和导航:菜单项可以是叶子,也可以包含子菜单,权限过滤、排序和渲染都可以统一处理。
- 组织架构:员工、团队、部门、公司形成树状结构,统计人数、预算、权限时递归汇总。
- 评论系统:评论和回复都可以作为节点,渲染、折叠、删除、计数使用统一接口。
- 权限模型:角色、权限组、单个权限组成树,判断权限时逐层聚合。
- 表达式树:规则引擎里单个条件和
AND/OR条件组都实现同一个evaluate()。 - 图层系统:设计工具里图形、图片、文本、分组图层都能移动、隐藏、复制和导出。
- 任务拆解:项目、里程碑、任务、子任务使用统一接口,进度和工时从叶子向上汇总。
- AST:编译器或 Markdown 解析器把文档表示成树,文本、标题、列表、代码块都作为节点处理。