设计模式-结构型模式:组合模式

简介: 设计模式-结构型模式:组合模式

1、简介

组合模式(Composite Pattern)是一种结构型设计模式,其目的是将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性

2、结构

组合模式包含三个角色:

  • 抽象组件(Component):它是组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component子部件
  • 叶子组件(Leaf):它在组合结构中表示叶节点对象,叶子组件没有子组件
  • 组合组件(Composite):它在组合结构中表示分支节点对象,它有子组件

组合模式常用于构建树形结构,如文件和文件夹管理器、组织结构图、组织架构等。

3、优缺点

组合模式的优点:

  • 它允许客户端将单个对象和组合对象一视同仁。
  • 新增和删除子节点非常方便。
  • 使客户端代码变得简单,因为客户端可以处理单个对象和组合对象一视同仁。

组合模式的缺点:

  • 由于组合模式中会使用到大量的递归,所以在处理大量数据时会导致程序运行速度变慢。
  • 由于组合模式中每个组件都会继承抽象组件,所以类的数量会增加。

4、使用场景

组合模式的适用场景:

  • 在需要表示一个对象整体或部分层次结构时,使用组合模式。
  • 当您需要支持在合适的时候增加新构件的能力时。

组合模式有很多变体,如树形组件、透明组合模式、安全组合模式等。组合模式在实际应用中非常常见,如文件和文件夹管理器、组织结构图、组织架构等。

实际应用:

通常用于构建树形结构:

  • 文件和文件夹管理系统:文件和文件夹都可以看作一个节点,文件夹中可以包含文件和其他文件夹,这样就形成了一个树形结构。
  • 组织结构图:组织构成树形结构,部门和员工都可以看作一个节点,部门可以包含其他部门和员工,这样就形成了一个树形结构。

5、代码实现

组合模式通常使用抽象类或接口来定义组件类。

Java代码实现步骤如下:

5.1、抽象组件(Component)

首先,我们可以定义一个抽象组件类,如下:

1. public abstract class Component {
2.  protected String name;
3. 
4.  public Component(String name) {
5.    this.name = name;
6.  }
7. 
8.  public abstract void add(Component component);
9.  public abstract void remove(Component component);
10.   public abstract void display(int depth);
11. }

这个类包含了对子组件的管理方法,如add、remove和display。

然后,我们可以定义叶子组件类和容器组件类。叶子组件类不能再包含子组件,而容器组件类可以包含子组件。

5.2、叶子组件(Leaf)

叶子组件类示例:

1. public class Leaf extends Component {
2. public Leaf(String name) {
3. super(name);
4.     }
5. 
6. @Override
7. public void add(Component component) {
8.         System.out.println("Cannot add to a leaf");
9.     }
10. 
11. @Override
12. public void remove(Component component) {
13.         System.out.println("Cannot remove from a leaf");
14.     }
15. 
16. @Override
17. public void display(int depth) {
18. StringBuilder sb = new StringBuilder();
19. for (int i = 0; i < depth; i++) {
20.             sb.append("-");
21.         }
22.         System.out.println(sb.toString() + name);
23.     }
24. }

5.3、组合组件(Composite)

容器组件类示例:

1. import java.util.ArrayList;
2. import java.util.List;
3. 
4. public class Composite extends Component {
5. private List<Component> children = new ArrayList<>();
6. 
7. public Composite(String name) {
8. super(name);
9.     }
10. 
11. @Override
12. public void add(Component component) {
13.         children.add(component);
14.     }
15. 
16. @Override
17. public void remove(Component component) {
18.         children.remove(component);
19.     }
20. 
21. @Override
22. public void display(int depth) {
23. StringBuilder sb = new StringBuilder();
24. for (int i = 0; i < depth; i++) {
25.             sb.append("-");
26.         }
27.         System.out.println(sb.toString() + name);
28. 
29. for (Component component : children) {
30.             component.display(depth + 2);
31.         }
32.     }
33. }

5.4、client

客户端可以使用相同的方法来管理叶子组件和容器组件。

例如,在下面的示例中,我们可以创建一个根容器组件,并在其中添加叶子组件和其他容器组件:

1. Component root = new Composite("root");
2. Component branch1 = new Composite("branch1");
3. root.add(branch1);
4. root.add(new Leaf("leaf1"));
5. 
6. Component branch2 = new Composite("branch2");
7. branch1.add(new Leaf("leaf2"));
8. branch1.add(branch2);
9. branch2.add(new Leaf("leaf3"));
10. 
11. root.display(1);

5.5、demo->end

最终结果:

5.6、代码总结

组合模式允许客户端以统一的方式处理个别对象和组合对象,并且可以使组合对象和个别对象具有相同的层次结构。这种模式对于构建树形结构很有用。

这只是组合模式的一种简单的实现方式,在实际项目中,根据需要可能需要对组件类进行更多的抽象和拓展。

6、深入理解代码案例

6.1、树状图

上述代码的树状图如下:

6.2、debug模式分析

6.2.1、打上断点

对第五行代码打上断点,开始debug,如下:

6.2.2、Component root = new Composite("root");

此时进入Composite类,准备调父类Component的有参构造器,代码如下:

6.2.2.1、super(name);

此时把"root"赋值给name,即Component root对象此时拥有name属性"root"

6.2.3、Component branch1 = new Composite("branch1");

创建对象流程与上面的root相同,此处不再说明

6.2.4、children集合

private List<Component> children = new ArrayList<>();

此时可以明显得知,root对象中包含了两个对象:branch1和left1

(此处要注意,left1是叶子,即它没有子组件)

6.2.4.1、Composite对象和Leaf对象的add()的区别

可以看到,Composite对象把传进来的Component对象添加进了集合中,但是Leaf对象并没有。

这符合叶子节点不再创建子组件的原则。

此处的区别,后面会再作说明

6.2.5、Component branch2 = new Composite("branch2");

此时创建branch2对象,这是目前第三个Composite对象。

然后可以看到,接下来,

branch1添加了两个对象:leaf2和branch2,

而branch2又添加了一个leaf3叶子对象

6.2.6、梳理关系脉络

此时各个对象之间的关系已经明朗,可以得到一开始的树形图。

需要注意的是,每new一个Composite对象,都自身背后会new一个ArrayList集合来存放该Composite对象的子节点对象,即:

       root->branch1、leaf1

       branch1->leaf2、branch2

       branch2->leaf3

下一步即是多次递归操作!

6.2.7、最后一步 : 递归

最终由root对象调递归函数!

6.2.7.1、display(int depth)

递归函数来自Composite类重写的父类Componet递归方法:

这是一个简单的代码实现案例,所以此处的递归也只是作举例示用。该递归方法主要思路是获取调用递归方法的对象的集合(children),然后用foreach遍历,在遍历中不断找子对象。

但是如果获取递归方法的对象是Leaf叶子对象,则不会再往下创建子组件,即children集合中没有对象

6.2.7.2、root

root调用display方法后,显然控制台会输出"-root",由上面的叙述可以知道,root中的集合包含两个对象

6.2.7.3、root->branch1

此时由branch1对象,调用递归方法,需要注意的是,这时候的depth为:1+2=3

显然,此时控制台输出的是"---branch1",这时候branch1包含两个对象:leaf2和branch2。

由之前的代码执行顺序来看,leaf2对象比branch2先创建,则此时增强for循环中的第一个对象,应该是leaf2。此时由leaf2调用递归方法,需要注意的是,此时的depth为:3+2=5。

所以此时控制台接着输出的是"-----leaf2"由于Leaf2是叶子组件,则不再创建子组件,回到for循环。

6.2.7.4、root->branch1->branch2

然后接着遍历来到branch1的第二个对象:branch2。此时的depth依然为5,显然,此时控制台接着输出的是"-----branch2"。

branch2对象中包含了一个叶子组件:leaf3。

此时进入for循环,有且仅有一次循环!此时的depth为:5+2=7。

6.2.7.5、root->branch1->branch2->leaf3

此时控制台接着输出的是"-------leaf2",叶子节点结尾,没有再任何其他组件生成,至此root的左子树全部走完,逐层回退,退回第一步for循环遍历root对象中的集合的时候,如图:

此时遍历root中第二个对象leaf1,leaf1为叶子节点,此时的depth为3(同branch1时一样),则控制台最终输出结果为"---leaf1"

至此,debug完成,root的左右子树全部走完,递归结束

6.3、总结

虽然代码复杂度较高,但是该模式可以在运行时动态地组合组件,并对整棵树进行遍历。可以在运行时动态地组合组件,并对整棵树进行遍历。它适用于反映对象整体。

相关文章
|
5月前
|
设计模式 JavaScript 前端开发
js设计模式【详解】—— 组合模式
js设计模式【详解】—— 组合模式
61 7
|
3月前
|
设计模式 Java
Java设计模式:组合模式的介绍及代码演示
组合模式是一种结构型设计模式,用于将多个对象组织成树形结构,并统一处理所有对象。例如,统计公司总人数时,可先统计各部门人数再求和。该模式包括一个通用接口、表示节点的类及其实现类。通过树形结构和节点的通用方法,组合模式使程序更易扩展和维护。
Java设计模式:组合模式的介绍及代码演示
|
3月前
|
设计模式 存储 安全
Java设计模式-组合模式(13)
Java设计模式-组合模式(13)
|
4月前
|
设计模式 存储 Java
【十】设计模式~~~结构型模式~~~享元模式(Java)
文章详细介绍了享元模式(Flyweight Pattern),这是一种对象结构型模式,通过共享技术实现大量细粒度对象的重用,区分内部状态和外部状态来减少内存中对象的数量,提高系统性能。通过围棋棋子的设计案例,展示了享元模式的动机、定义、结构、优点、缺点以及适用场景,并探讨了单纯享元模式和复合享元模式以及与其他模式的联用。
【十】设计模式~~~结构型模式~~~享元模式(Java)
|
4月前
|
设计模式 存储 Java
【九】设计模式~~~结构型模式~~~外观模式(Java)
文章详细介绍了外观模式(Facade Pattern),这是一种对象结构型模式,通过引入一个外观类来简化客户端与多个子系统之间的交互,降低系统的耦合度,并提供一个统一的高层接口来使用子系统。通过文件加密模块的实例,展示了外观模式的动机、定义、结构、优点、缺点以及适用场景,并讨论了如何通过引入抽象外观类来提高系统的可扩展性。
【九】设计模式~~~结构型模式~~~外观模式(Java)
|
4月前
|
设计模式 Java
【八】设计模式~~~结构型模式~~~装饰模式(Java)
文章详细介绍了装饰模式(Decorator Pattern),这是一种对象结构型模式,用于在不使用继承的情况下动态地给对象添加额外的职责。装饰模式通过关联机制,使用装饰器类来包装原有对象,并在运行时通过组合的方式扩展对象的行为。文章通过图形界面构件库的设计案例,展示了装饰模式的动机、定义、结构、优点、缺点以及适用场景,并提供了Java代码实现和应用示例。装饰模式提高了系统的灵活性和可扩展性,适用于需要动态、透明地扩展对象功能的情况。
【八】设计模式~~~结构型模式~~~装饰模式(Java)
|
4月前
|
设计模式 XML 存储
【七】设计模式~~~结构型模式~~~桥接模式(Java)
文章详细介绍了桥接模式(Bridge Pattern),这是一种对象结构型模式,用于将抽象部分与实现部分分离,使它们可以独立地变化。通过实际的软件开发案例,如跨平台视频播放器的设计,文章阐述了桥接模式的动机、定义、结构、优点、缺点以及适用场景,并提供了完整的代码实现和测试结果。桥接模式适用于存在两个独立变化维度的系统,可以提高系统的可扩展性和灵活性。
【七】设计模式~~~结构型模式~~~桥接模式(Java)
|
4月前
|
设计模式 XML 存储
【六】设计模式~~~结构型模式~~~适配器模式(Java)
文章详细介绍了适配器模式(Adapter Pattern),这是一种结构型设计模式,用于将一个类的接口转换成客户期望的另一个接口,使原本不兼容的接口能够一起工作,提高了类的复用性和系统的灵活性。通过对象适配器和类适配器两种实现方式,展示了适配器模式的代码应用,并讨论了其优点、缺点以及适用场景。
|
4月前
|
设计模式 缓存 Java
【十一】设计模式~~~结构型模式~~~代理模式(Java)
文章详细介绍了代理模式(Proxy Pattern),这是一种对象结构型模式,用于给对象提供一个代理以控制对它的访问。文中阐述了代理模式的动机、定义、结构、优点、缺点和适用环境,并探讨了远程代理、虚拟代理、保护代理等不同代理形式。通过一个商务信息查询系统的实例,展示了如何使用代理模式来增加身份验证和日志记录功能,同时保持客户端代码的无差别对待。此外,还讨论了代理模式在分布式技术和Spring AOP中的应用,以及动态代理的概念。
【十一】设计模式~~~结构型模式~~~代理模式(Java)
|
6月前
|
设计模式 存储 安全
Java设计模式:组合模式之透明与安全的两种实现(七)
Java设计模式:组合模式之透明与安全的两种实现(七)