深度解析设计模式之组合模式

时间:2022-02-20 02:31:42

深度解析设计模式之组合模式

一、介绍

组合模式(Composite Pattern),又叫部分整体模式,依据树形结构来组合对象,用来表示部分以及整体层次。

组合模式 一般用来描述整体与部分的关系,它将对象组织到树形结构中,最顶层的节点称为根节点,根节点下面可以包含树枝节点和叶子节点,树枝节点下面又可以包含树枝节点和叶子节点。如下图所示:

深度解析设计模式之组合模式

在组合模式中,会把树枝节点和叶子节点认为是同一种数据类型(用同一接口定义),让它们具备一致行为。

这样,整个树形结构中的对象都是同一种类型,带来的一个好处就是客户无需辨别 树枝节点还是叶子节点,而是可以直接进行操作,给客户使用带来极大的便利。

从设计的角度看,组合模式涉及到三个角色:

抽象根节点:它是一个抽象接口,定义了算法;

具体节点:实现或继承自抽象根节点,完成具体算法操作;

客户端:客户类提出使用具体类的请求;

二、示例

下面,我们拿学校的组织架构为例,比如说一个学校,包含了后勤部、网络部、教学部、保卫部、分校等部门组成,每一个分校,同样具有后勤部、网络部这些。既然这些部门都是学校的部门,基本的操作应该都是一样的,我们可以将所有的部门都拉入学校属性。

用类图表示如下:

深度解析设计模式之组合模式

实现过程如下!

  1. /**
  2. *学校接口
  3. */
  4. publicinterfaceSchool{
  5.  
  6. /**
  7. *添加分校或者部门
  8. *@paramschool
  9. */
  10. voidaddPart(Schoolschool);
  11.  
  12. /**
  13. *移除分校或者部门
  14. *@paramschool
  15. */
  16. voidremovePart(Schoolschool);
  17.  
  18. /**
  19. *展示分校或者部门信息
  20. */
  21. voiddisplayPart();
  22. }

然后,创建一个学校具体实现类ConcreteSchool,可以是总校,也可以是分校,如下:

  1. /**
  2. *具体学校,可以是总校,也可以是分校
  3. */
  4. publicclassConcreteSchoolimplementsSchool{
  5.  
  6. privateStringname;//名称
  7.  
  8. privateListpartList=newArrayList<>();
  9.  
  10. publicConcreteSchool(Stringname){
  11. this.name=name;
  12. }
  13.  
  14. @Override
  15. publicvoidaddPart(Schoolschool){
  16. partList.add(school);
  17. }
  18.  
  19. @Override
  20. publicvoidremovePart(Schoolschool){
  21. partList.remove(school);
  22. }
  23.  
  24. /**
  25. *学校查看部门信息
  26. */
  27. @Override
  28. publicvoiddisplayPart(){
  29. for(Schoolschool:partList){
  30. school.displayPart();
  31. }
  32.  
  33. }
  34. }

接着,创建两个具体的部门,网络部门InternetDepartment、安全部门SecurityDepartment,代码如下:

  1. /**
  2. *网络部门
  3. */
  4. publicclassInternetDepartmentimplementsSchool{
  5.  
  6. privateStringname;//名称
  7.  
  8. publicInternetDepartment(Stringname){
  9. this.name=name;
  10. }
  11.  
  12. @Override
  13. publicvoidaddPart(Schoolschool){}
  14.  
  15. @Override
  16. publicvoidremovePart(Schoolschool){}
  17.  
  18. @Override
  19. publicvoiddisplayPart(){
  20. System.out.println("我是"+name+",负责学校的网络管理");
  21. }
  22. }
  1. /**
  2. *安全部门
  3. */
  4. publicclassSecurityDepartmentimplementsSchool{
  5.  
  6. privateStringname;//名称
  7.  
  8. publicSecurityDepartment(Stringname){
  9. this.name=name;
  10. }
  11.  
  12. @Override
  13. publicvoidaddPart(Schoolschool){}
  14.  
  15. @Override
  16. publicvoidremovePart(Schoolschool){}
  17.  
  18. @Override
  19. publicvoiddisplayPart(){
  20. System.out.println("我是"+name+",负责学校的安全工作");
  21. }
  22. }

最后,编写一个测试类,如下:

  1. publicclassCompositeClient{
  2.  
  3. publicstaticvoidmain(String[]args){
  4. //总校部门
  5. ConcreteSchoolrootSchool=newConcreteSchool("总校");
  6. rootSchool.addPart(newInternetDepartment("总校网络部门"));
  7. rootSchool.addPart(newSecurityDepartment("总校安全部门"));
  8.  
  9. //分校部门
  10. ConcreteSchoolbranchSchool=newConcreteSchool("分校");
  11. branchSchool.addPart(newInternetDepartment("分校网络部门"));
  12. branchSchool.addPart(newSecurityDepartment("分校安全部门"));
  13.  
  14. rootSchool.addPart(branchSchool);
  15. rootSchool.displayPart();//展示信息
  16. }
  17. }

输出结果:

  1. 我是总校网络部门,负责学校的网络管理
  2. 我是总校安全部门,负责学校的安全工作
  3. 我是分校网络部门,负责学校的网络管理
  4. 我是分校安全部门,负责学校的安全工作

从上面的例子,可以很清晰看到类的层次关系,所有的具体对象当作一个单一的对象School来处理。

三、应用

在 Java 的 GUI 容器组件中,就用到了组合模式,所有的子类组件,都可以看作为容器对象。

深度解析设计模式之组合模式

当然,还有我们使用的 Mybatis 在处理动态 SQL 节点时,也应用到了组合设计模式,Mybatis 会将映射配置文件中定义的动态 SQL 节点、文本节点等解析成对应的 SqlNode 实现,并形成树形结构。

四、总结

当想表达对象的部分-整体的层次结构时,推荐采用组合模式进行设计。

五、参考

1、java的架构师技术栈 - 23种设计模式之组合模式

2、菜鸟教程 -组合模式

原文链接:https://mp.weixin.qq.com/s/1PJUDnG4Dpozn_5lmSHPFA