组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。
大话设计模式中程杰老师给出的定义是,组合模式:将对象组合成树形结构以表示”部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
组合模式结构图:
首先,让我们通过过一个简单的实例来简单了解一下组合模式做基本的用法。实例演示了一个组织中员工的层次结构。
创建Employee类:
package com.exercise.composite;
import java.util.ArrayList;
import java.util.List;
public class Employee {
private String name;
private String dept;
private int salary;
private List subordinates;//部下
//constructor
public Employee(String name, String dept, int salary,
List subordinates) {
super();
this.name = name;
this.dept = dept;
this.salary = salary;
this.subordinates = subordinates;
subordinates = new ArrayList();
}
public Employee(String name, String dept, int salary) {
super();
this.name = name;
this.dept = dept;
this.salary = salary;
subordinates = new ArrayList();
}
public void add(Employee e){
subordinates.add(e);
}
public void remove(Employee e){
subordinates.remove(e);
}
public List getSubordinates(){
return subordinates;
}
public String toString(){
return "Employee :[ Name : " + name
+ ", dept : " + dept + ", salary :"
+ salary + " ]";
}
}
测试类:
package com.exercise.composite;
/**
* 使用Employee来创建和打印员工的层次结构
* @author lmb
*
*/
public class CompositePatternDemo {
public static void main(String[] args) {
Employee CEO = new Employee("John","CEO", 30000);
Employee headSales = new Employee("Robert","Head Sales", 20000);
Employee headMarketing = new Employee("Michel","Head Marketing", 20000);
Employee clerk1 = new Employee("Laura","Marketing", 10000);
Employee clerk2 = new Employee("Bob","Marketing", 10000);
Employee salesExecutive1 = new Employee("Richard","Sales", 10000);
Employee salesExecutive2 = new Employee("Rob","Sales", 10000);
CEO.add(headSales);
CEO.add(headMarketing);
headSales.add(salesExecutive1);
headSales.add(salesExecutive2);
headMarketing.add(clerk1);
headMarketing.add(clerk2);
//打印该组织的所有员工
System.out.println("-------------------公司员工情况----------------------");
System.out.println(CEO);
for (Employee headEmployee : CEO.getSubordinates()) {
//打印CEO的直属一级部下
System.out.println(headEmployee);
for (Employee employee : headEmployee.getSubordinates()) {
//打印CEO的二级部下
System.out.println(employee);
}
}
}
}
运行结果:
使用场景:
需求中是体现部分与整体层次结构的时候,以及当我们希望用户可以忽略组合对象与单个对象的不同,统一的使用组合结构中的所有对象时。简而言之,就是涉及到部分、整体场景时,如树形菜单,文件、文件夹的管理。
主要解决:它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
如何解决:树枝和叶子实现统一接口,树枝内部组合该接口。
一般在工作中说起部分和总体我们想到最多的大概就是总公司和分公司了,如果我们需要做一套办公管理系统,并且总公司的人力资源部、财务部等的办公挂历功能在所有的分公司也都要有,我们该怎么实现?
功能实现:
抽象公司类或接口
package com.composite;
public abstract class Company {
protected String name;
public Company(String name){
this.name = name;
}
public abstract void add(Company company);//add
public abstract void remove(Company company);//remove
public abstract void display(int depth);//display
public abstract void lineofDuty();//line of duty
}
具体公司类(树枝节点)
package com.composite;
import java.util.ArrayList;
import java.util.List;
public class ConcreteCompany extends Company {
private List childrenCompany = new ArrayList();
public ConcreteCompany(String name) {
super(name);
}
@Override
public void add(Company company) {
childrenCompany.add(company);
}
@Override
public void display(int depth) {
System.out.println("第 " + depth + " 层的机构名为: " + name);
for (Company c : childrenCompany) {
c.display(depth + 1);
}
}
@Override
public void lineofDuty() {
for (Company c : childrenCompany) {
c.lineofDuty();
}
}
@Override
public void remove(Company company) {
childrenCompany.remove(company);
}
}
财务部和人力资源部(树叶节点)
package com.composite;
public class HRDepartment extends Company {
public HRDepartment(String name) {
super(name);
}
@Override
public void add(Company company) {
}
@Override
public void display(int depth) {
System.out.println("第 " + depth + " 层的机构名为: " + name);
}
@Override
public void lineofDuty() {
System.out.println(name + " 负责员工招聘管理培训");
}
@Override
public void remove(Company company) {
}
}
package com.composite;
public class FinanceDepartment extends Company {
public FinanceDepartment(String name) {
super(name);
}
@Override
public void add(Company company) {
}
@Override
public void display(int depth) {
System.out.println("第 " + depth + " 层的机构名为: " + name);
}
@Override
public void lineofDuty() {
System.out.println(name + " 负责公司财务收支管理");
}
@Override
public void remove(Company company) {
}
}
测试方法
package com.composite;
public class CompositePatternDemo {
public static void main(String[] args) {
//一个总公司
ConcreteCompany root = new ConcreteCompany("北京总公司");
root.add(new HRDepartment("总公司人力资源部"));
root.add(new FinanceDepartment("总公司财务部"));
//三个子公司
ConcreteCompany com1 = new ConcreteCompany("广州分公司");
com1.add(new HRDepartment("广州分公司人力资源部"));
com1.add(new FinanceDepartment("广州分公司财务部"));
root.add(com1);
ConcreteCompany com2 = new ConcreteCompany("杭州分公司");
com2.add(new HRDepartment("杭州分公司人力资源部"));
com2.add(new FinanceDepartment("杭州分公司财务部"));
root.add(com2);
ConcreteCompany com3 = new ConcreteCompany("深圳分公司");
com3.add(new HRDepartment("深圳分公司人力资源部"));
com3.add(new FinanceDepartment("深圳分公司财务部"));
root.add(com3);
System.out.println("-------公司结构图--------");
root.display(1);
System.out.println("----------各部门职责----------");
root.lineofDuty();
}
}
运行结果:
这样,通过组合模式我们就定义了包含人力资源部和财务部这些基本对象和分公司等组合对象的类层次结构。
基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断的递归下去,在客户代码中,任何用到基本对象的地方都可以使用组合对象了。组合模式让客户可以一致的使用组合结构和单个对象。
应用实例:
1、算术表达式包括操作数、操作符和另一个操作数,其中,另一个操作符也可以是操作树、操作符和另一个操作数。
2、在 JAVA AWT 和 SWING 中,对于 Button 和 Checkbox 是树叶,Container 是树枝。
优点:
1、高层模块调用简单。
2、节点自由增加。
缺点:
在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。