一、什么是组合模式
组合模式(Composite),将对象组合成树形构造以示意“局部 - 整体”的层次结构,用户对单个对象和组合对象的应用具备一致性。
所以当咱们的案例是树形构造或者是局部 - 整体的关系时,就能够思考应用组合模式。
组合模式有两种不同的实现,别离为通明模式和平安模式,上面将具体阐明一下两种实现的区别。
先阐明一下 UML 图中各角色的职责。Component 是对象申明接口,在适当状况下,实现所有类共有接口的默认行为;Leaf 是叶子节点对象,其没有子节点;Composite 是树枝节点对象,用来存储部件,组合树枝节点和叶子节点造成一个树形构造。
上面这两种形式咱们共用同一套客户端,先将客户端代码放上。
1 public class Client {
2
3 public static void main(String[] args) {
4 // 创立根节点及其子节点
5 Composite root = new Composite("root");
6 root.add(new Leaf("Leaf A"));
7 root.add(new Leaf("Leaf B"));
8
9 // 创立第二层节点及其子节点
10 Composite branch = new Composite("Composite X");
11 branch.add(new Leaf("Leaf XA"));
12 branch.add(new Leaf("Leaf XB"));
13 root.add(branch);
14
15 // 创立第三层节点及其子节点
16 Composite branch2 = new Composite("Composite XY");
17 branch2.add(new Leaf("Leaf XYA"));
18 branch2.add(new Leaf("Leaf XYB"));
19 branch.add(branch2);
20
21 // 创立第二层节点
22 root.add(new Leaf("Leaf C"));
23
24 // 创立第二层节点并删除
25 Leaf leaf = new Leaf("Leaf D");
26 root.add(leaf);
27 root.remove(leaf);
28
29 // 打印
30 root.display(1);
31 }
32
33 }
二、组合模式之通明模式
通明模式是把组合应用的办法放到抽象类中,不论叶子对象还是树枝对象都有雷同的构造,这样做的益处就是叶子节点和树枝节点对于外界没有区别,它们具备完全一致的行为接口。但因为 Leaf 类自身不具备 add()、remove() 办法的性能,所以实现它是没有意义的。UML 结构图如下:
1. Component
1 public abstract class Component {
2
3 protected String name;
4
5 public Component(String name) {
6 this.name = name;
7 }
8
9 // 减少一个叶子构件或树枝构件
10 public abstract void add(Component component);
11
12 // 删除一个叶子构件或树枝构件
13 public abstract void remove(Component component);
14
15 // 获取分支下的所有叶子构件和树枝构件
16 public abstract void display(int depth);
17
18 }
2. Composite
1 public class Composite extends Component {
2
3 public Composite(String name) {4 super(name);
5 }
6
7 // 构建容器
8 private ArrayList<Component> componentArrayList = new ArrayList<Component>();
9
10 @Override
11 public void add(Component component) {12 this.componentArrayList.add(component);
13 }
14
15 @Override
16 public void remove(Component component) {17 this.componentArrayList.remove(component);
18 }
19
20 @Override
21 public void display(int depth) {
22 // 输入树形构造
23 for(int i=0; i<depth; i++) {24 System.out.print('-');
25 }
26 System.out.println(name);
27
28 // 上级遍历
29 for (Component component : componentArrayList) {30 component.display(depth + 1);
31 }
32 }
33
34 }
3. Leaf
1 public class Leaf extends Component {
2
3 public Leaf(String name) {4 super(name);
5 }
6
7 @Override
8 public void add(Component component) {
9 // 空实现,抛出“不反对申请”异样
10 throw new UnsupportedOperationException();
11 }
12
13 @Override
14 public void remove(Component component) {
15 // 空实现,抛出“不反对申请”异样
16 throw new UnsupportedOperationException();
17 }
18
19 @Override
20 public void display(int depth) {
21 // 输入树形构造的叶子节点
22 for(int i=0; i<depth; i++) {23 System.out.print('-');
24 }
25 System.out.println(name);
26 }
27
28 }
通过组合模式输入一个树形构造,运行后果如下:
三、组合模式之平安模式
平安模式是把树枝节点和树叶节点彻底离开,树枝节点独自领有用来组合的办法,这种办法比拟平安。但因为不够通明,所以树叶节点和树枝节点将不具备雷同的接口,客户端的调用须要做相应的判断,带来了不便。UML 结构图如下:
1. Component
这里相比通明模式就少了 add() 和 romove() 形象办法的申明。
1 public abstract class Component {
2
3 protected String name;
4
5 public Component(String name) {
6 this.name = name;
7 }
8
9 // 获取分支下的所有叶子构件和树枝构件
10 public abstract void display(int depth);
11
12 }
2. Composite
这里 add() 和 remove() 办法的实现就从继承变为了本人实现。
1 public class Composite extends Component {
2
3 public Composite(String name) {4 super(name);
5 }
6
7 // 构建容器
8 private ArrayList<Component> componentArrayList = new ArrayList<Component>();
9
10 // 减少一个叶子构件或树枝构件
11 public void add(Component component) {12 this.componentArrayList.add(component);
13 }
14
15 // 删除一个叶子构件或树枝构件
16 public void remove(Component component) {17 this.componentArrayList.remove(component);
18 }
19
20 @Override
21 public void display(int depth) {
22 // 输入树形构造
23 for(int i=0; i<depth; i++) {24 System.out.print('-');
25 }
26 System.out.println(name);
27
28 // 上级遍历
29 for (Component component : componentArrayList) {30 component.display(depth + 1);
31 }
32 }
33
34 }
3. Leaf
叶子节点中没有了空实现,比拟平安。
1 public class Leaf extends Component {
2
3 public Leaf(String name) {4 super(name);
5 }
6
7 @Override
8 public void display(int depth) {
9 // 输入树形构造的叶子节点
10 for(int i=0; i<depth; i++) {11 System.out.print('-');
12 }
13 System.out.println(name);
14 }
15
16 }
运行后果如下:
由此可看出两个办法是雷同的运行后果,区别在于外部实现不同,一种是叶节点与树枝节点具备统一的行为接口但有空实现的通明模式,另一种是树枝节点独自领有用来组合的办法但调用不便的平安模式。
为什么说它调用不便呢,因为咱们如果通过递归遍历树时,这时须要判断以后节点是叶子节点还是树枝节点,客户端就须要相应的判断。
四、组合模式的利用
1. 何时应用
- 想表白“局部 - 整体”层次结构(树形构造)时
- 心愿用户疏忽组合对象与单个对象的不同,用户将对立的应用组合构造中的所有对象
2. 办法
- 树枝和叶子实现对立接口,树枝外部组合该接口
3. 长处
- 高层模块调用简略。一棵树形机构中的所有节点都是 Component,部分和整体对调用者来说没有任何区别,高层模块不用关怀本人解决的是单个对象还是整个组合构造。
- 节点自在减少
4. 毛病
- 应用组合模式时,其叶子和树枝的申明都是实现类,而不是接口,违反了依赖倒转准则
5. 应用场景
- 保护和展现局部 - 整体关系的场景(如树形菜单、文件和文件夹治理)
- 从一个整体中可能独立出局部模块或性能的场景
五、组合模式的实现
上面咱们以公司的层级构造为例,先看一下这个例子中该公司的层级构造(该例选自卑话设计模式——程杰著)。
这种局部与整体的关系,咱们就能够思考应用组合模式,上面采纳组合模式的通明模式对其实现,UML 图如下:
1. 具体公司类
此为树枝节点,实现增加、移除、显示和履行职责四种办法。
1 public class ConcreteCompany extends Company {
2
3 private List<Company> companyList = new ArrayList<Company>();
4
5 public ConcreteCompany(String name) {6 super(name);
7 }
8
9 @Override
10 public void add(Company company) {11 this.companyList.add(company);
12 }
13
14 @Override
15 public void remove(Company company) {16 this.companyList.remove(company);
17 }
18
19 @Override
20 public void display(int depth) {
21 // 输入树形构造
22 for(int i=0; i<depth; i++) {23 System.out.print('-');
24 }
25 System.out.println(name);
26
27 // 上级遍历
28 for (Company component : companyList) {29 component.display(depth + 1);
30 }
31 }
32
33 @Override
34 public void lineOfDuty() {
35 // 职责遍历
36 for (Company company : companyList) {37 company.lineOfDuty();
38 }
39 }
40
41 }
2. 人力资源部
叶子节点,add 和 remove 办法空实现。
1 public class HRDepartment extends Company {
2
3 public HRDepartment(String name) {4 super(name);
5 }
6
7 @Override
8 public void add(Company company) {
9
10 }
11
12 @Override
13 public void remove(Company company) {
14
15 }
16
17 @Override
18 public void display(int depth) {
19 // 输入树形构造的子节点
20 for(int i=0; i<depth; i++) {21 System.out.print('-');
22 }
23 System.out.println(name);
24 }
25
26 @Override
27 public void lineOfDuty() {28 System.out.println(name + ": 员工招聘培训治理");
29 }
30
31 }
3. 财务部
叶子节点,add 和 remove 办法空实现。
1 public class FinanceDepartment extends Company {
2
3 public FinanceDepartment(String name) {4 super(name);
5 }
6
7 @Override
8 public void add(Company company) {
9
10 }
11
12 @Override
13 public void remove(Company company) {
14
15 }
16
17 @Override
18 public void display(int depth) {
19 // 输入树形构造的子节点
20 for(int i=0; i<depth; i++) {21 System.out.print('-');
22 }
23 System.out.println(name);
24 }
25
26 @Override
27 public void lineOfDuty() {28 System.out.println(name + ": 公司财务收支治理");
29 }
30
31 }
4. Client 客户端
1 public class Client {
2
3 public static void main(String[] args) {
4 // 总公司
5 ConcreteCompany root = new ConcreteCompany("北京总公司");
6 root.add(new HRDepartment("总公司人力资源部"));
7 root.add(new FinanceDepartment("总公司财务部"));
8
9 // 分公司
10 ConcreteCompany company = new ConcreteCompany("上海华东分公司");
11 company.add(new HRDepartment("华东分公司人力资源部"));
12 company.add(new FinanceDepartment("华东分公司财务部"));
13 root.add(company);
14
15 // 办事处
16 ConcreteCompany company1 = new ConcreteCompany("南京办事处");
17 company1.add(new HRDepartment("南京办事处人力资源部"));
18 company1.add(new FinanceDepartment("南京办事处财务部"));
19 company.add(company1);
20
21 ConcreteCompany company2 = new ConcreteCompany("杭州办事处");
22 company2.add(new HRDepartment("杭州办事处人力资源部"));
23 company2.add(new FinanceDepartment("杭州办事处财务部"));
24 company.add(company2);
25
26 System.out.println("结构图:");
27 root.display(1);
28
29 System.out.println("\n 职责:");
30 root.lineOfDuty();
31 }
32
33 }
运行后果如下:
组合模式这样就定义了蕴含人力资源部和财务部这些根本对象和分公司、办事处等组合对象的类层次结构。
根本对象能够被组合成更简单的组合对象,而这个组合对象又能够被组合,这样一直地递归上来,客户代码中,任何用到根本对象的中央都能够应用组合对象了。
这里用了通明模式,用户不必关怀到底是解决一个叶节点还是解决一个组合组件,也就用不着为定义组合而写一些抉择判断语句了。简略点说就是组合模式能够让客户统一地应用组合构造和单个对象。
关注公众号:java 宝典