结构型模式 - 组合模式(多个同类型子对象组成整体)
组合模式(Composite Pattern)
简介
组合模式,也叫部分整体模式,用于把一组相似的对象当作一个单一的对象,组合模式依据树形结构来组合对象,用来表示部分以及整体层次。属于结构型模式,创建了对象组的树形结构。
意图
将对象组合树形结构以表示“部分-整体”层次结构。使得用户对单个对象和组合对象的使用具有一致性。
解决问题
主要解决:在树形结构问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
如何解决:树枝和叶子实现统一接口,树枝内部组合该接口。
关键代码:树枝和叶子实现统一接口,树枝内部组合该接口。
特点
- 优点
- 高层模块调用简单;
- 节点自由增加。
-
缺点
使用组合模式时,叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则 -
组合模式有两种实现方式
-
透明方式
在Component中声明所有用来管理子对象的方法,其中包括Add, Remove等,这样实现Component接口的所有子类都具备了Add和Remove。这样做的好处是叶节点和枝节点对于外界没有区别,具备完全一致的行为接口。但问题也在这里,因为Leaf类本身不具备Add, Remove功能,实现它没有意义。 -
不透明方式
也是安全的方式,在Component接口中,不去声明Add和Remove方法,那么子类Leaf也不需要去实现。
组合模式 VS 建造者模式
组合模式和建造者模式都是由子对象构成整体,那么它们有什么区别呢?
- 组成部分类型
组合模式强调组成整体的部分,都是同一类型。建造者模式主要是为了表示,通过子部件组装称为完成产品,不要求子部件都是同一类型。 - 组成部分和整体的关系
组合模式体现是整体与部分的层次关系,依据树形结构来组合对象。建造者模式体现的是这个过程,对客户屏蔽创建对象的复杂性。 - 组成部分的可变性
组合模式如果缺少某个部分,整体依然是整体,而且容易添加、删除部分;建造者模式如果缺少某个部分,很可能无法正常工作,无法删除子部件。
应用实例
1.算术表达式包括操作数、操作符和另一个操作数,其中,另一个操作符也可以是操作数、操作符和另一个操作数。
2.JAVA AWT和SWING中,对于Button和Checkbox是树叶,Container是树枝。
通用类图
透明方式类图
不透明方式类图
实现代码
以不透明方式为例
- 新建Company抽象类,声明管理子对象的方法
// Company.java
public abstract class Company {
protected String name;
public Company(String name) {
this.name = name;
}
public abstract void add(Company company);
public abstract void remove(Company company);
public abstract void display(int dept); // 显示
public abstract void lineOfDuty(); // 职责
}
- 新建具体的公司类ConcreteCompany实现Company
// ConcreteCompany.java
public class ConcreteCompany extends Company {
private List<Company> children = new ArrayList<Company>();
public ConcreteCompany(String name) {
super(name);
}
@Override
public void add(Company company) {
children.add(company);
}
@Override
public void remove(Company company) {
children.remove(company);
}
@Override
public void display(int dept) {
int tmp = dept;
while(tmp -- > 0)
System.out.print("-");
System.out.println(name);
for(Company component: children) {
component.display(dept + 2);
}
}
@Override
public void lineOfDuty() {
for(Company component: children) {
component.lineOfDuty();
}
}
}
- 新建叶子节点HRDepartment, FinanceDepartment
// HRDepartment.java
public class HRDepartment extends Company {
public HRDepartment(String name) {
super(name);
}
@Override
public void add(Company company) {
}
@Override
public void remove(Company company) {
}
@Override
public void display(int dept) {
while(dept -- > 0)
System.out.print("-");
System.out.println(name);
}
@Override
public void lineOfDuty() {
System.out.println(name + " 员工招聘管理培训");
}
}
// FinanceDepartment.java
public class FinanceDepartment extends Company {
public FinanceDepartment(String name) {
super(name);
}
@Override
public void add(Company company) {
}
@Override
public void remove(Company company) {
}
@Override
public void display(int dept) {
while(dept -- > 0)
System.out.print("-");
System.out.println(name);
}
@Override
public void lineOfDuty() {
System.out.println(name + " 公司财务收支管理");
}
}
- 新建客户端类,使用组合模式创建叶子节点和Component节点,并打印层次结构图和职责
// Client.java
public class Client {
public static void main(String[] args) {
ConcreteCompany root = new ConcreteCompany("北京总公司");
root.add(new HRDepartment("总公司人力资源部"));
root.add(new FinanceDepartment("总公司财务部"));
ConcreteCompany comp = new ConcreteCompany("上海华东分公司");
comp.add(new HRDepartment("华东分公司人力资源部"));
comp.add(new FinanceDepartment("华东分公司财务部"));
root.add(comp);
ConcreteCompany comp1 = new ConcreteCompany("南京办事处");
comp1.add(new HRDepartment("南京办事处人力资源部"));
comp1.add(new FinanceDepartment("南京办事处财务部"));
root.add(comp1);
ConcreteCompany comp2 = new ConcreteCompany("杭州办事处");
comp2.add(new HRDepartment("杭州办事处人力资源部"));
comp2.add(new FinanceDepartment("杭州办事处财务部"));
root.add(comp2);
System.out.println("结构图:");
root.display(1);
System.out.println("职责:");
root.lineOfDuty();
}
}
运行结果
结构图:
-北京总公司
---总公司人力资源部
---总公司财务部
---上海华东分公司
-----华东分公司人力资源部
-----华东分公司财务部
---南京办事处
-----南京办事处人力资源部
-----南京办事处财务部
---杭州办事处
-----杭州办事处人力资源部
-----杭州办事处财务部
职责:
总公司人力资源部 员工招聘管理培训
总公司财务部 公司财务收支管理
华东分公司人力资源部 员工招聘管理培训
华东分公司财务部 公司财务收支管理
南京办事处人力资源部 员工招聘管理培训
南京办事处财务部 公司财务收支管理
杭州办事处人力资源部 员工招聘管理培训
杭州办事处财务部 公司财务收支管理