组合模式(Composite)
介绍
组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
意图:将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
主要解决:它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
何时使用: 1、您想表示对象的部分-整体层次结构(树形结构)。 2、您希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
如何解决:树枝和叶子实现统一接口,树枝内部组合该接口。
关键代码:树枝内部组合该接口,并且含有内部属性 List,里面放 Component。
应用实例: 1、算术表达式包括操作数、操作符和另一个操作数,其中,另一个操作数也可以是操作数、操作符和另一个操作数。 2、在 JAVA AWT 和 SWING 中,对于 Button 和 Checkbox 是树叶,Container 是树枝。
优点: 1、高层模块调用简单。 2、节点自由增加。
缺点:在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。
使用场景:部分、整体场景,如树形菜单,文件、文件夹的管理。
注意事项:定义时为具体类。
实现
组合模式有两种不同的实现,分别为透明模式和安全模式,下面将详细说明一下两种实现的区别。
组件(Component)接口描述了树中简单项目和复杂项目所共有的操作。
叶节点(Leaf)是树的基本结构,它不包含子项目。一般情况下,叶节点最终会完成大部分的实际工作,因为它们无法将工作指派给其他部分。
容器(Container)——又名“组合(Composite)”——是包含叶节点或其他容器等子项目的单位。容器不知道其子项目所属的具体类,它只通过通用的组件接口与其子项目交互。容器接收到请求后会将工作分配给自己的子项目,处理中间结果,然后将最终结果返回给客户端。
客户端(Client)通过组件接口与所有项目交互。因此,客户端能以相同方式与树状结构中的简单或复杂项目交互。
组合模式之透明模式
透明模式是把组合使用的方法放到抽象类中,不管叶子对象还是树枝对象都有相同的结构,这样做的好处就是叶子节点和树枝节点对于外界没有区别,它们具备完全一致的行为接口。但因为Leaf类本身不具备add()、remove()方法的功能,所以实现它是没有意义的。
1. Component
public abstract class Component {
protected String name;
public Component(String name) {
this.name = name;
}
//增加一个叶子构件或树枝构件
public abstract void add(Component component);
//删除一个叶子构件或树枝构件
public abstract void remove(Component component);
//获取分支下的所有叶子构件和树枝构件
public abstract void display(int depth);
}2. Composite
public class Composite extends Component {
public Composite(String name) {
super(name);
}
//构建容器
private ArrayList<Component> componentArrayList = new ArrayList<Component>();
@Override
public void add(Component component) {
this.componentArrayList.add(component);
}
@Override
public void remove(Component component) {
this.componentArrayList.remove(component);
}
@Override
public void display(int depth) {
//输出树形结构
for(int i=0; i<depth; i++) {
System.out.print('-');
}
System.out.println(name);
//下级遍历
for (Component component : componentArrayList) {
component.display(depth + 1);
}
}
}3. Leaf
public class Leaf extends Component {
public Leaf(String name) {
super(name);
}
@Override
public void add(Component component) {
//空实现,抛出“不支持请求”异常
throw new UnsupportedOperationException();
}
@Override
public void remove(Component component) {
//空实现,抛出“不支持请求”异常
throw new UnsupportedOperationException();
}
@Override
public void display(int depth) {
//输出树形结构的叶子节点
for(int i=0; i<depth; i++) {
System.out.print('-');
}
System.out.println(name);
}
}组合模式之安全模式
安全模式是把树枝节点和树叶节点彻底分开,树枝节点单独拥有用来组合的方法,这种方法比较安全。但由于不够透明,所以树叶节点和树枝节点将不具有相同的接口,客户端的调用需要做相应的判断,带来了不便。
1. Component
这里相比透明模式就少了add()和romove()抽象方法的声明。
public abstract class Component {
protected String name;
public Component(String name) {
this.name = name;
}
//获取分支下的所有叶子构件和树枝构件
public abstract void display(int depth);
}2. Composite
这里add()和remove()方法的实现就从继承变为了自己实现。
public class Composite extends Component {
public Composite(String name) {
super(name);
}
//构建容器
private ArrayList<Component> componentArrayList = new ArrayList<Component>();
//增加一个叶子构件或树枝构件
public void add(Component component) {
this.componentArrayList.add(component);
}
//删除一个叶子构件或树枝构件
public void remove(Component component) {
this.componentArrayList.remove(component);
}
@Override
public void display(int depth) {
//输出树形结构
for(int i=0; i<depth; i++) {
System.out.print('-');
}
System.out.println(name);
//下级遍历
for (Component component : componentArrayList) {
component.display(depth + 1);
}
}
}3. Leaf
叶子节点中没有了空实现,比较安全。
public class Leaf extends Component {
public Leaf(String name) {
super(name);
}
@Override
public void display(int depth) {
//输出树形结构的叶子节点
for(int i=0; i<depth; i++) {
System.out.print('-');
}
System.out.println(name);
}
}Client
这两种方式我们共用同一套客户端代码
public class Client {
public static void main(String[] args) {
//创建根节点及其子节点
Composite root = new Composite("root");
root.add(new Leaf("Leaf A"));
root.add(new Leaf("Leaf B"));
//创建第二层节点及其子节点
Composite branch = new Composite("Composite X");
branch.add(new Leaf("Leaf XA"));
branch.add(new Leaf("Leaf XB"));
root.add(branch);
//创建第三层节点及其子节点
Composite branch2 = new Composite("Composite XY");
branch2.add(new Leaf("Leaf XYA"));
branch2.add(new Leaf("Leaf XYB"));
branch.add(branch2);
//创建第二层节点
root.add(new Leaf("Leaf C"));
//创建第二层节点并删除
Leaf leaf = new Leaf("Leaf D");
root.add(leaf);
root.remove(leaf);
//打印
root.display(1);
}
}换一个思路思考
有时候换成 pid 来实现,数据结构的存储可能更简单
// 笔记
export interface IFile {
// Note id
nid: string
// Parent id
pid: string
// 用户的_id
userId: string
// 创建时间
createdAt: number
// 更新时间
updateAt: number
// 是否文件
isFile: boolean
}