组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。
简单来说:将对象组合成树形结构以表示整体-部分结构的层次结构。Composite使用户对单个对象和组合对象的使用具有一致性。
如果一个对象包含另一个对象的引用,称这样的对象为组合对象。如果将当前组合对象作为一个整体的话,那么它所包含的对象就是该整体的一部分。如果一个对象不包含有其他对象的引用,称这样的对象为个体对象。在编写程序时,我们希望将许多个体对象和组合对象组成树形结构,一次表示部分-整体的层次结构,并借助该层次结构使得用户能够用一致的方式处理个体对象和组合对象。在组成的树形结构中,个体对象和组合对象都是树中的节点,但是组合对象具有其他子节点的节点,个体对象是不具有其他子节点的叶节点,也就是说在树形结构中,组合对象所包含的对象将作为该组合对象的子节点被对待。
部分、整体场景,如树形菜单,文件、文件夹的管理。
组合模式包括三种角色。
抽象组件(Component): 是一个接口(抽象类),该接口(抽象类)定义了个体对象和组合对象需要实现的关于操作其子节点的方法,比如add()、remove()以及getChild()等方法。抽象组件也可以定义个体对象和组合对象用于操作器自身的方法,比如isLeaf()方法等。
Component节点(Component Node): 实现Component接口类的实例,Component节点不仅实现Component接口,而且可以含有其他Component节点或Leaf节点的引用。
Leaf节点(Leaf Node): 实现了Component接口类的实例,Leaf节点实现Component接口,不可以含有其他Component节点或Leaf节点的引用,因此叶节点在实现Component接口有关操作子节点的方法时,比如add(),remove()和getChild()方法,可让方法抛出一个异常,也可以实现为空操作。
以下通过一个简单的问题来描述怎样使用组合模式,这个问题就是用组合模式描述连队的军士结构,并计算军饷。一个连队由一个连长,两个排长,6个班长和60个士兵锁构成,一共69人。连长直接指挥2个排长,每个排长直接指挥3个班长,每个班长直接指挥10个士兵。连长的军饷是每月5000,排长是4000元,连长是2000元,士兵是1000元。现在使用功组合模式,让连队的军士形成树形结构,并计算一个班的军饷,一个连的军饷和整个连队的军饷。
1.抽象组件
本问题中,抽象组件(Component)的名字是MilitaryPerson,MilitaryPerson接口的代码如下所示:
import java.util.Iterator;
public interface MilitaryPerson {
public void add(MilitaryPerson person);
public void remove(MilitaryPerson person);
public MilitaryPerson getChild(int index);
public Iterator<MilitaryPerson> getAllChildren();
public boolean isLeaf();
public double getSalary();
public void setSalary(double salary);
}
2.Composite节点
对于本问题,Composite节点是MilitaryOfficer类的实例,MilitaryOfficer类的代码如下:
import java.util.Iterator;
import java.util.LinkedList;
public class MilitaryOfficer implements MilitaryPerson{
LinkedList<MilitaryPerson> list;
String name;
double salary;
public MilitaryOfficer(String name, double salary) {
this.name = name;
this.salary = salary;
list=new LinkedList<MilitaryPerson>();
}
@Override
public void add(MilitaryPerson person) {
list.add(person);
}
@Override
public void remove(MilitaryPerson person) {
list.remove(person);
}
@Override
public MilitaryPerson getChild(int index) {
return list.get(index);
}
@Override
public Iterator<MilitaryPerson> getAllChildren() {
return list.iterator();
}
@Override
public boolean isLeaf() {
return false;
}
@Override
public double getSalary() {
return salary;
}
@Override
public void setSalary(double salary) {
this.salary=salary;
}
}
3.Leaf节点
对于本问题,Leaf节点是MilitarySoldier类的实例,MilitarySoldier类的代码如下所示:
import java.util.Iterator;
public class MilitarySoldier implements MilitaryPerson{
//由于是叶子节点 无字节点队列
double salary;
String name;
public MilitarySoldier(double salary, String name) {
this.salary = salary;
this.name = name;
}
@Override
public void add(MilitaryPerson person) {
//由于无字节点 空实现
}
@Override
public void remove(MilitaryPerson person) {
//由于无字节点 空实现
}
@Override
public MilitaryPerson getChild(int index) {
return null;
}
@Override
public Iterator<MilitaryPerson> getAllChildren() {
return null;
}
@Override
public boolean isLeaf() {
return true;
}
@Override
public double getSalary() {
return salary;
}
@Override
public void setSalary(double salary) {
this.salary=salary;
}
}
4.测试程序
import java.util.Iterator;
public class Application {
public static void main(String[] args) {
MilitaryPerson 连长=new MilitaryOfficer("连长",5000);
MilitaryPerson 排长1=new MilitaryOfficer("一排长",4000);
MilitaryPerson 排长2=new MilitaryOfficer("二排长",4000);
MilitaryPerson 班长11=new MilitaryOfficer("一班长",2000);
MilitaryPerson 班长12=new MilitaryOfficer("二班长",2000);
MilitaryPerson 班长13=new MilitaryOfficer("三班长",2000);
MilitaryPerson 班长21=new MilitaryOfficer("一班长",2000);
MilitaryPerson 班长22=new MilitaryOfficer("二班长",2000);
MilitaryPerson 班长23=new MilitaryOfficer("三班长",2000);
MilitaryPerson []士兵=new MilitarySoldier[60];
for (int i = 0; i < 士兵.length; i++) {
士兵[i]=new MilitarySoldier("小兵",1000);
}
连长.add(排长1);
连长.add(排长2);
排长1.add(班长11);
排长1.add(班长12);
排长1.add(班长13);
排长2.add(班长21);
排长2.add(班长22);
排长2.add(班长23);
for (int i = 0; i < 10; i++) {
班长11.add(士兵[i]);
班长12.add(士兵[i+10]);
班长13.add(士兵[i+20]);
班长21.add(士兵[i+30]);
班长22.add(士兵[i+40]);
班长23.add(士兵[i+50]);
}
System.out.println("一排的军饷"+ComputerSalary.computerSalary(排长1));
System.out.println("一班的军饷"+ComputerSalary.computerSalary(班长11));
System.out.println("一连的军饷"+ComputerSalary.computerSalary(连长));
}
}
class ComputerSalary{//计算工资类
public static double computerSalary(MilitaryPerson person){
double sum=0;
if(person.isLeaf()){
sum+=person.getSalary();
}else {
sum+=person.getSalary();
Iterator<MilitaryPerson> iterator = person.getAllChildren();
while(iterator.hasNext()){
MilitaryPerson p = iterator.next();
sum+=computerSalary(p);
}
}
return sum;
}
}
1.组合模式中包含个体对象和组合对象,并形成树形结构,使用户可以方便地处理个体对象和组合对象;
2.组合对象和个体对象实现了相同的接口,用户一般无须区别个体对象和组合对象;