大家好✋,我是知识汲取者😄,今天给大家带来一篇有关组合模式的学习笔记。众所周知能够熟练使用设计模式是一个优秀程序猿的必备技能,当我们在项目中选择一个或多个合适的设计模式,不仅能大大提高项目的稳健性、可移植性、可维护性,同时还能让你的代码更加精炼,具备艺术美感。
组合模式是一种很神奇的模式,它可以让用户无需关心当前是组合对象、还是单个对象,从而大大提高程序的灵活性。通过本文您可以知道组合模式是什么(What)?为什么要使用组合模式(Why)组合模式要怎么使用(How)?话不多说,让我们一起来学习吧(●ˇ∀ˇ●)
推荐阅读:
组合模式是什么?
组合模式(Composite Pattern),又叫部分整体模式,是一种结构型模式,它组合多个对象形成树形结构以表示具有“整体—部分”关系的层次结
组合模式的作用:让单个对象和组合对象的使用具有一致性
组合模式的优缺点
优点:
……
缺点:
……
组合模式的适用场景:
……
Java中的应用:我们适用Document获取结点时,就是使用了组合模式;Mybatis在处理动态SQL节点时,应用到了组合设计模式,MyBatis会将映射文件中定义的静态SQL节点、文本节点等解析成对应的SqlNode实现,形成树形结构;JavaSE中的AWT和Swing包的设计也用到了组合模式
组合模式的角色划分
组合模式的分类
示例:
问题描述:公司组织结构 在学习和使用组合模式时,Sunny软件公司开发人员发现树形结构其实随处可见,例如Sunny公司的组织结构就是“一棵标准的树”,如图所示:
![]()
在Sunny软件公司的内部办公系统Sunny OA系统中,有一个与公司组织结构对应的树形菜单,行政人员可以给各级单位下发通知,这些单位可以是总公司的一个部门,也可以是一个分公司,还可以是分公司的一个部门。用户只需要选择一个根节点即可实现通知的下发操作,而无须关心具体的实现细节。

温馨提示:通过上面的类图可以发现测试类(这里模拟客户端)交互的类太多了,这是由于在Test中要创建大量的对象,其实这里也可以使用工厂模式进行优化,让测试类只与工厂类进行交互,从而进一步降低耦合度,但这里只是为了体验组合模式就不改造了,感兴趣的读者可以自行改造
Step1:创建抽象构件
Unit:
package com.hhxy.composite.transparent;
/**
* @author ghp
* @date 2022/10/7
* @title 公司和部门的抽象类
* @description
*/
public abstract class Unit {
/*
* 发通知的方法
*/
public abstract void notice();
/**
* 添加部门或公司的方法
*/
public abstract void add(unit unit);
/**
* 移除部门或公司的方法
*/
public abstract void remove(int i);
}
Step2:创建容器构件
Company:
package com.hhxy.composite.transparent.ext;
import com.hhxy.composite.transparent.Unit;
import java.util.ArrayList;
import java.util.List;
/**
* @author ghp
* @date 2022/10/7
* @title 公司(这是一个容器)
* @description 用于存储公司对象或者部门对象,是组合模式的核心
*/
public class Company extends Unit {
private String name;
private List<Unit> companies = new ArrayList<>();
public Company(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 发通知的方法
*/
@Override
public void notice() {
for (Unit company : companies) {
company.notice();
}
}
/**
* 添加部门或公司的方法
*
* @param company
*/
@Override
public void add(Unit company) {
companies.add(company);
}
/**
* 移除部门或公司的方法
*
* @param i
*/
@Override
public void remove(int i) {
companies.remove(i);
}
}
Step3:创建叶子构件
1)FinanceDpartment:
package com.hhxy.composite.transparent.ext;
import com.hhxy.composite.transparent.Unit;
/**
* @author ghp
* @date 2022/10/7
* @title 财务部
* @description
*/
public class FinanceDepartment extends Unit {
private String name;
public FinanceDepartment(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 发通知的方法
*/
@Override
public void notice() {
System.out.println(name+"财务部发了通知");
}
/**
* 添加部门或公司的方法
*
* @param unit
*/
@Override
public void add(Unit unit) {
System.out.println("不支持该方法");
}
/**
* 移除部门或公司的方法
*
* @param i
*/
@Override
public void remove(int i) {
System.out.println("不支持该方法");
}
}
2)HumanResourceDepartment:
和FinanceDpartmen的代码类似,具体代码请参考Gitee或Github仓库,略……
3)ResercheDepartment:
和FinanceDpartmen的代码类似,具体代码请参考Gitee或Github仓库,略……
Step4:测试
package com.hhxy.test;
import com.hhxy.composite.transparent.Unit;
import com.hhxy.composite.transparent.ext.Company;
import com.hhxy.composite.transparent.ext.FinanceDepartment;
import com.hhxy.composite.transparent.ext.HumanResourcesDepartment;
import com.hhxy.composite.transparent.ext.ResearchDepartment;
/**
* @author ghp
* @date 2022/10/7
* @title 测试类1
* @description 用于测试透明组合模式
*/
public class Test1 {
public static void main(String[] args) {
//创建三家公司对象,Sunny总公司、上海分公司、深圳总公司
Unit company1,company2,company3;
//创建部门对象,财务部、人力资源部、研发部
Unit department1,department2,department3,department4,department5,department6,department7,department8,department9;
company1 = new Company("Sunny总公司");
company2 = new Company("上海分公司");
company3 = new Company("深圳分公司");
company1.add(company2);
company1.add(company3);
department1 = new FinanceDepartment("Sunny总公司");
department2 = new HumanResourcesDepartment("Sunny总公司");
department3 = new ResearchDepartment("Sunny总公司");
company1.add(department1);
company1.add(department2);
company1.add(department3);
department4 = new FinanceDepartment("上海分公司");
department5 = new HumanResourcesDepartment("上海分公司");
department6 = new ResearchDepartment("上海分公司");
company2.add(department4);
company2.add(department5);
company2.add(department6);
department7 = new FinanceDepartment("深圳分公司");
department8 = new HumanResourcesDepartment("深圳分公司");
department9 = new ResearchDepartment("深圳分公司");
company3.add(department7);
company3.add(department8);
company3.add(department9);
//总公司发通知
company1.notice();
System.out.println("-----------------------------------");
//上海分公司发通知
company2.notice();
System.out.println("-----------------------------------");
//深圳分公司发通知
company3.notice();
System.out.println("-----------------------------------");
//模拟用户操作失误
department1.add(department2);
}
}
测试结果:

示例:
在透明组合模式的示例基础上进行改进,相对于透明组合模式而言,该进的地方是:在抽象构件中,我们不定义所有的行为,而是只定义叶子构件和容器构件所公有的行为,也就是Notice方法。容器构件独有的方法只在容器构件中定义并进行实现,这样就能保障用户无法使用叶子构件对象去调用到容器构件的特有方法了

Step1:创建抽象构件
Util:
package com.hhxy.composite.safe;
/**
* @author ghp
* @date 2022/10/9
* @title
* @description
*/
public abstract class Unit {
/**
* 发通知的方法
*/
public abstract void notice();
}
Step2:创建容器构件
Company:
package com.hhxy.composite.safe.ext;
import com.hhxy.composite.safe.Unit;
import java.util.ArrayList;
import java.util.List;
/**
* @author ghp
* @date 2022/10/9
* @title 公司类(是一个容器)
* @description 用于存储公司对象和部门对象
*/
public class Company extends Unit {
private String name;
private List<Unit> companies = new ArrayList<>();
public Company(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 发通知的方法
*/
@Override
public void notice() {
for (Unit company : companies) {
company.notice();
}
}
/**
* 添加部门或公司的方法
*/
public void add(Unit company) {
companies.add(company);
}
/**
* 移除部门或公司的方法
*/
public void remove(int i) {
companies.remove(i);
}
}
Step3:创建叶子构件
1)FinanceDpartment:
package com.hhxy.composite.safe.ext;
import com.hhxy.composite.safe.Unit;
/**
* @author ghp
* @date 2022/10/7
* @title 财务部
* @description
*/
public class FinanceDepartment extends Unit {
private String name;
public FinanceDepartment(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 发通知的方法
*/
@Override
public void notice() {
System.out.println(name+"财务部发了通知");
}
}
2)HumanResourceDepartment:
和FinanceDpartmen的代码类似,具体代码请参考Gitee或Github仓库,略……
3)ResercheDepartment:
和FinanceDpartmen的代码类似,具体代码请参考Gitee或Github仓库,略……
Step4:测试
package com.hhxy.test;
import com.hhxy.composite.safe.Unit;
import com.hhxy.composite.safe.ext.*;
/**
* @author ghp
* @date 2022/10/9
* @title 测试类2
* @description 用于测试安全组合模式
*/
public class Test2 {
public static void main(String[] args) {
//创建三家公司对象,Sunny总公司、上海分公司、深圳总公司
Company company1,company2,company3;
//创建部门对象,财务部、人力资源部、研发部
Unit department1,department2,department3,department4,department5,department6,department7,department8,department9;
company1 = new Company("Sunny总公司");
company2 = new Company("上海分公司");
company3 = new Company("深圳分公司");
company1.add(company2);
company1.add(company3);
department1 = new FinanceDepartment("Sunny总公司");
department2 = new HumanResourcesDepartment("Sunny总公司");
department3 = new ResearchDepartment("Sunny总公司");
company1.add(department1);
company1.add(department2);
company1.add(department3);
department4 = new FinanceDepartment("上海分公司");
department5 = new HumanResourcesDepartment("上海分公司");
department6 = new ResearchDepartment("上海分公司");
company2.add(department4);
company2.add(department5);
company2.add(department6);
department7 = new FinanceDepartment("深圳分公司");
department8 = new HumanResourcesDepartment("深圳分公司");
department9 = new ResearchDepartment("深圳分公司");
company3.add(department7);
company3.add(department8);
company3.add(department9);
//总公司发通知
company1.notice();
System.out.println("-----------------------------------");
//上海分公司发通知
company2.notice();
System.out.println("-----------------------------------");
//深圳分公司发通知
company3.notice();
//模拟用户操作失误
// department1.add(department2);//直接在编译阶段报错,这样就能防止用户的误操作
}
}
测试结果:

组合模式是一种结构型模式,它让对象和对象之间形成树形结构,在Java中应用很广泛,并拥有两种类别的组合模式
透明组合模式,面对抽象编程,符合“依赖倒置原则”,让程序对用户透明,用户不必关心具体实现,降低了程序的使用难度。但是却不符合”单一职责原则“与”接口隔离原则“,叶子构件拥有不属于它的方法,用户容易操作失误,需要增加异常处理代码、
安全组合模式,虽然解决了安全性问题,但是他违背了“依赖依赖导致原则”,没有面对抽象编程,增加了系统的维护成本
总的来讲两种类别的组合模式,各有优劣,面对具体情况、具体分析,我们要做的就是熟悉它们各自的特点,掌握它们的实现方式,面对类似问题,可以选择一种合适的组合模式来解决问题😃
自此,文章就结束了,如果觉得本文对你有一丢丢帮助的话😄,欢迎点赞👍+评论✍,您的支持将是我写出更加优秀文章的动力O(∩_∩)O

上一篇:每日一个设计模式之【桥接模式】
下一篇:每日一个设计模式之【装饰模式】
参考文章:
在次致谢