模式概念
树形目录结构中,包含文件和文件夹两类不同的元素,文件夹可以包含文件,也可以继续包含子文件夹,在文件中不能包含子文件或者文件夹。在这里文件夹可以看作是一个容器(Container) ,文件可以看作是叶子(Leaf) ,如何将容器对象和叶子对象进行递归组合,使用户在使用时无需区分,这就是今天要说到的组合模式。
组合模式(Composite Pattern) 是一种对象结构型设计模式,它允许你将对象组合成树形结构来表示“部分-整体”的层次结构,因此组合模式又叫部分-整体模式。
模式结构
- Component(抽象类、组件) :定义了容器中所有对象的一致操作方式,可以是抽象类或接口。
- Leaf(叶节点) :实现 Component 接口,不包含子节点,是组合中的末端节点。
- Composite(容器节点) :也实现 Component 接口,同时包含其他 Component 对象,可以是叶节点或其他容器节点。
- Client(客户端) :通过 Component 接口与组合结构交互。
代码实现
// 抽象接口
class Component {
constructor(name) {
this.name = name;
this.children = [];
}
add(component) {
this.children.push(component);
}
remove(component) {
this.children = this.children.filter(child => child !== component);
}
operate() {
this.children.forEach(child => child.operate());
}
}
// 叶节点
class Leaf extends Component {
operate() {
console.log(`Leaf: ${this.name}`);
}
}
// 容器节点
class Composite extends Component {
operate() {
console.log(`Composite: ${this.name}`);
super.operate();
}
}
// 使用
const root = new Composite("Root");
const leaf1 = new Leaf("Leaf1");
const leaf2 = new Leaf("Leaf2");
const branch1 = new Composite("Branch1");
const branch2 = new Composite("Branch2");
root.add(branch1);
root.add(branch2);
branch1.add(leaf1);
branch2.add(leaf2);
console.log("root", root); //包含name和children的属性结构对象
root.operate(); // 输出: Composite: Root, Composite: Branch1, Leaf: Leaf1, Composite: Branch2, Leaf: Leaf2
Leaf和Composite对象可以统一使用operate方法进行操作。- 客户端代码可以对整个对象树进行递归操作,无需关心对象的具体类型。
模式效果
模式优点:
- 高灵活性:可以对整个树形结构进行统一的操作,使得用户对单个对象和组合对象的使用具有一致性,无需关心是叶节点还是容器节点,清楚地定义分层次的复杂对象,让客户端忽略层次的差异,方便对整个层次结构的控制。
- 易于扩展:可以轻松地添加新的叶节点和容器节点类型,符合开闭原则。
- 新方案:为树形结构的面向对象实现提供了一种灵活的解决方案,在树形结构问题中,模糊了简单元素和复杂元素的概念,可以像处理简单元素一样处理复杂元素,从而使得程序和复杂元素的内部解耦,无需关心有多少层,调用时只需要在
根部进行调用。
模式缺点:
- 难以限制:增加新节点时,很难对节点类型进行限制。
- 理解难度:对于初学者来说,理解树形结构和递归操作可能有些困难。
模式应用
我们可以在以下场景中使用组合模式:
- 在具有整体和部分的层次结构中,希望通过一种方式忽略它们之间的差异,使得客户端可以一致的对待他们。
- 在需要处理树形结构时。
- 在面对分离出叶子对象和容器对象的程序时,如果他们的类型不固定,需要扩展新的类型时。
导航菜单
导航菜单是 Web 应用中常见的组件,用于帮助用户快速找到并切换到不同的页面或功能模块。导航菜单的设计使用组合模式思想可以很好的表示菜单项之间的层次关系,并且方便管理。
我们可以将菜单项看作是叶子节点、容器节点。菜单项表示为抽象类,菜单和菜单目录分别是叶子节点和容器节点,每个菜单项可以包含子菜单项,形成一个树形结构。
const items: MenuItem[] = [
{
key: 'sub1',
label: 'Navigation One',
icon: <MailOutlined />,
children: [
{
key: 'g1',
label: 'Item 1',
type: 'group',
children: [
{ key: '1', label: 'Option 1' },
{ key: '2', label: 'Option 2' },
],
},
{
key: 'g2',
label: 'Item 2',
type: 'group',
children: [
{ key: '3', label: 'Option 3' },
{ key: '4', label: 'Option 4' },
],
},
],
},
{
key: 'sub2',
label: 'Group',
type: 'group',
children: [
{ key: '13', label: 'Option 13' },
{ key: '14', label: 'Option 14' },
],
}
];
除此之外,像平时可能接触到的业务中也有组合模式的思想:
- 文件管理器:文件和文件夹可以构成一个树形结构。组合模式可以用于实现文件和文件夹的统一操作,如添加、删除、重命名和排序。
- 组织结构管理:对于企业应用中的组织结构管理,员工和部门可以构成一个树形结构。组合模式可以帮助实现对员工和部门的统一管理,如查询、权限分配和统计分析。
在需要处理树形结构的场景中,组合模式是一个非常合适的选择。
今天的分享就到这里,希望可以帮助到你!假如你对文章感兴趣,可以来我的公众号:小新学研社。