1. 组合模式概念
组合模式(Composite Pattern)也称为整体-部分(Part-Whole)模式,它的宗旨是通过将单个 对象(叶子节点)和组合对象(树枝节点)用相同的接口进行表示,使得客户对单个对象和组合对象的 使用具有一致性,属于结构型模式
- 组合关系与聚合关系的区别:
组合关系:胳膊和身体的关系,胳膊不能脱离身体而独立存在,胳膊只能算是身体的一部分(具有相同的生命周期)。
聚合关系:一个老师有很多学生,但是每一个学生又属于多个老师(具有不同的生命周期)。
&emps;&emps;组合模式一般用来描述整体与部分的关系,它将对象组织到树形结构中,最顶层的节点称为根节点, 根节点下面可以包含树枝节点和叶子节点,树枝节点下面又可以包含树枝节点和叶子节点。如下图所示:
由上图可以看出,其实根节点和树枝节点本质上是同一种数据类型,可以作为容器使用;而叶子节 点与树枝节点在语义上不属于同一种类型,但是在组合模式中,会把树枝节点和叶子节点认为是同一种 数据类型(用同一接口定义),让它们具备一致行为。这样,在组合模式中,整个树形结构中的对象都 是同一种类型,带来的一个好处就是客户无需辨别树枝节点还是叶子节点,而是可以直接进行操作,给客户使用带来极大的便利。
-组合模式包含 的角色
- 抽象根节点(Component)
定义系统各层次对象的共有方法和属性,可以预先定义一些默认 行为和属性;
- 树枝节点(Composite)
定义树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成 一个树形结构;
- 叶子节点(Leaf)
叶子节点对象,其下再无分支,是系统层次遍历的最小单位。 组合模式 在代码具体实现上,有两种不同的方式,分别是透明组合模式和安全组合模式
2. 组合模式应用场景
组合模式主要总结为以下应用场景:
1、希望客户端可以忽略组合对象与单个对象的差异时;
2、对象层次具备整体和部分,呈树形结构。 在我们生活中的组合模式也非常常见,比如树形菜单,操作系统目录结构,公司组织架构等
当子系统与其内各个对象层次呈现树形结构时,可以使用组合模式让子系统内各个对象层次的行为 操作具备一致性。客户端使用该子系统内任意一个层次对象时,无须进行区分,直接使用通用操作即可,为客户端的使用带来了便捷。
如果树形结构系统不使用组合模式进行架构,那么按照正常的思维逻辑,对该系统进行职责分析,按上文树形结 构图所示,该系统具备两种对象层次类型:树枝节点和叶子节点。那么我们就需要构造两种对应的类型,然后由于树 枝节点具备容器功能,因此树枝节点类内部需维护多个集合存储其他对象层次(如:List,List), 如果当前系统对象层次更复杂时,那么树枝节点内就又要增加对应的层次集合,这对树枝节点的构建带来了巨大的复 杂性,臃肿性以及不可扩展性。同时客户端访问该系统层次时,还需进行层次区分,这样才能使用对应的行为,给客 户端的使用也带来了巨大的复杂性。而如果使用组合模式构建该系统,由于组合模式抽取了系统各个层次的共性行为, 具体层次只需按需实现所需行为即可,这样子系统各个层次就都属于同一种类型,所以树枝节点只需维护一个集合 (List)即可存储系统所有层次内容,并且客户端也无需区分该系统各个层次对象,对内系统架构简洁优 雅,对外接口精简易用。
3.组合模式透明写法
透明组合模式是把所有公共方法都定义在 Component 中,这样做的好处是客户端无需分辨是叶子 节点(Leaf)和树枝节点(Composite),它们具备完全一致的接口。
- 抽象根节点:
package com.gupaoedu.vip.pattern.composite.general.transparent;
/**
* 抽象根节点;抽取了系统各个层次的共性行为,把根节点和子节点的相同的行为取出来
*/
public abstract class Component {
protected String name;
public Component(String name){
this.name = name;
}
// 各个叶子节点自己的特性,定义为抽象方法,由叶子节点自行实现
public abstract String operation();
// 重要:addChild的入参一定是抽象根节点Component,这样就可以实现根节点添加树节点,树节点添加叶子节点,层层嵌套添加!
/*叶子节点显然不具备addChild的能力,但是这里也进行了空实现,子类可以去实现,
但是调用的时候又不让调,是一种不安全的写法,违背了最少知道原则*/
public boolean addChild(Component component) {
throw new UnsupportedOperationException("addChild not supported!");
}
public boolean removeChild(Component component){
throw new UnsupportedOperationException("removeChild not supported!");
}
public Component getCild(int index) {
throw new UnsupportedOperationException("getChild not supported!");
}
}
组合模式把所有可能用到的方法都定义到这个最顶层的抽象类中,但是不写任何逻辑处理的代码,而是直接 抛异常。这里为什么不用抽象方法?因为,用了抽象方法,其子类就必须实现, 这样便体现不出各子类的细微差异。因此,子类继承此抽象类后,只需要重写有差异的方法覆盖父类的方法即可。
- 树枝节点
package com.gupaoedu.vip.pattern.composite.general.transparent;
import java.util.ArrayList;
import java.util.List;
// 树节点
public class Composite extends Component {
//树枝节点只需维护一个抽象根节点的集合 (List<Component>)即可存储系统所有层次内容
private List<Component> components;
public Composite(String name) {
super(name);
this.components = new ArrayList<Component>();
}
// 节点的具体行为
@Override
public String operation() {
StringBuilder builder = new StringBuilder(this.name);
for (Component component : components) {
builder.append("\n").append(component.operation());
}
return builder.toString();
}
@Override
public boolean addChild(Component component) {
return this.components.add(component);
}
// 移除操作是将components中的元素移除
@Override
public boolean removeChild(Component component) {
return this.components.remove(component);
}
@Override
public Component getCild(int index) {
return this.components.get(index);
}
}
- 叶子节点:
package com.gupaoedu.vip.pattern.composite.general.transparent;
/**
* 叶子节点
* 不管是叶子节点,还是根节点,都继承抽象根节点Component
*/
public class Leaf extends Component {
public Leaf(String name) {
super(name);
}
@Override
public String operation() {
return this.name;
}
@Override
public boolean addChild(Component component) {
return super.addChild(component);
}
@Override
public boolean removeChild(Component component) {
return super.removeChild(component);
}
@Override
public Component getCild(int index) {
return super.getCild(index);
}
}
- 客户端调用
package com.gupaoedu.vip.pattern.composite.general.transparent;
public class Test {
public static void main(String[] args) {
// 创建一个根节点
Component root = new Composite("root");
// 创建两个树枝节点
Component branchA = new Composite("--BranchA");
Component branchB = new Composite("----BranchB");
// 创建三个叶子节点
Component leafA = new Leaf("----leafA");
Component leafB = new Leaf("------leafB");
Component leafC = new Leaf("--leafC");
root.addChild(branchA);
root.addChild(leafC);
branchA.addChild(leafA);
branchA.addChild(branchB);
branchB.addChild(leafB);
String result = root.operation();
System.out.println(result);
}
}
UML类图如下:
透明组合模式把所有公共方法都定义在 Component 中,这样做的好处是客户端无需分辨是叶子节点(Leaf)和树枝节点(Composite),它们具备完全一致的接口;缺点是叶子节点(Leaf)会继承 得到一些它所不需要(管理子类操作的方法)的方法,这与设计模式接口隔离原则相违背。
4.组合模式安全写法
安全组合模式是只规定系统各个层次的最基础的一致行为,而把组合(树节点)本身的方法(管理 子类对象的添加,删除等)放到自身当中。
-抽象根节点:
package com.gupaoedu.vip.pattern.composite.general.safe;
/**
* 抽象根节点;抽取了系统各个层次的共性行为,把根节点和子节点的相同的行为取出来
*/
public abstract class Component {
protected String name;
public Component(String name){
this.name = name;
}
// 各个叶子节点自己的特性,定义为抽象方法,由叶子节点自行实现
public abstract String operation();
// public boolean addChild(Component component) {
// throw new UnsupportedOperationException("addChild not supported!");
// }
//
// public boolean removeChild(Component component){
// throw new UnsupportedOperationException("removeChild not supported!");
// }
//
// public Component getCild(int index) {
// throw new UnsupportedOperationException("getChild not supported!");
// }
}
- 树枝节点
1.树枝节点要继承抽象根节点Component
2.树枝节点只需维护一个抽象根节点的集合 (List)即可存储系统所有层次内容
3.只有树枝节点才有addChild removeChild getCild等操作,叶子节点不具备,因此抽象根节点中去除这三个方法
package com.gupaoedu.vip.pattern.composite.general.safe;
import java.util.ArrayList;
import java.util.List;
// 树节点
public class Composite extends Component {
//树枝节点只需维护一个抽象根节点的集合 (List<Component>)即可存储系统所有层次内容
private List<Component> components;
public Composite(String name) {
super(name);
this.components = new ArrayList<Component>();
}
// 节点的具体行为
@Override
public String operation() {
StringBuilder builder = new StringBuilder(this.name);
for (Component component : components) {
builder.append("\n").append(component.operation());
}
return builder.toString();
}
public boolean addChild(Component component) {
return this.components.add(component);
}
// 移除操作是将components中的元素移除
public boolean removeChild(Component component) {
return this.components.remove(component);
}
public Component getCild(int index) {
return this.components.get(index);
}
}
- 叶子节点
package com.gupaoedu.vip.pattern.composite.general.safe;
/**
* 叶子节点
* 不管是叶子节点,还是根节点,都继承抽象根节点Component
*/
public class Leaf extends Component {
public Leaf(String name) {
super(name);
}
// 这里继承的时候只会重写operation
@Override
public String operation() {
return this.name;
}
}
UML类图: