将对象以树形结构组织起来,以达成“部分-整体” 的层次结构,使得客户端对单个对象和组合对象的使用具有一致性.
Composite模式理解:
想到Composite就应该想到树形结构图。组合体内这些对象都有共同接口,当组合体一个对象的方法被调用执行时,Composite将遍历(Iterator)整个树形结构,寻找同样包含这个方法的对象并实现调用执行。可以用牵一动百来形容。
Composite模式好处:
1.使客户端调用简单,客户端可以一致的使用组合结构或其中单个对象,用户就不必关系自己处理的是单个对象还是整个组合结构,这就简化了客户端代码。
2.更容易在组合体内加入对象部件. 客户端不必因为加入了新的对象部件而更改代码
如何使用:
Step 1:
首先定义一个接口或抽象类(设计模式通用方式),其他设计模式对接口内部定义限制不多,Composite却有个规定,那就是要在接口内部定义一个用于访问和管理Composite组合体对象的方法.
Step 2:
以抽象类或接口的方式定义一个供继承或实现的类或接口:
import java.util.Iterator; public abstract class Equipment { private String name; public Equipment(String name) { this.name = name; } // 实体价格 public abstract double netPrice(); // 折扣价格 public abstract double discountPrice(); // 增加部件的方法 public boolean add(Equipment equipment) { return false; } // 移除部件方法 public boolean remove(Equipment equipment) { return false; } // 组合体内访问各个部件的方法. public Iterator iter() { return null; } }
这个抽象类定义的就是一个组合体内所有对象都具有的共同方法和属性。
Step 3:
下面接着定义组合体内原件对象的实体类:
public class Disk extends Equipment { // 硬盘实体价格 public static double diskNetPrice = 2.0; // 硬盘折扣价格 public static double diskDiscountPrice = 1.0; public Disk(String name) { super(name); } @Override public double netPrice() { return diskNetPrice; } @Override public double discountPrice() { return diskDiscountPrice; } }
Disk是组合体内的一个对象,或称一个部件,这个部件是个单独元素( Primitive)。还有一种可能是,一个部件也是一个组合体,就是说这个部件下面还有'儿子',这是树形结构中通常的情况,应该比较容易理解。
Step 4:
现在我们在继续定义一个组合体:
import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; public abstract class CompositeEquipment extends Equipment { private int i = 0; // 定义一个List,用来保存组合体内的各个子对象. private List<Equipment> equipment = new ArrayList<Equipment>(); public CompositeEquipment(String name) { super(name); } public boolean add(Equipment equipment) { if (equipment instanceof Disk && this instanceof Chassis) { System.out.println("在盘盒里面放了一个硬盘"); } else if (equipment instanceof Chassis && this instanceof Cabinet) { System.out.println("在柜子里面放了一个盘盒"); } this.equipment.add(equipment); return true; } public double netPrice() { double netPrice = 0.; if (this instanceof Cabinet) { System.out.println("我是在柜子的组合对象里面.柜子本身价格为:" + Cabinet.cabinetNetPrice); } else if (this instanceof Chassis) { System.out.println("我是在盘盒的组合对象里面.盘盒本身价格为:" + Chassis.chassisNetPrice); } Iterator<Equipment> iter = equipment.iterator(); while (iter.hasNext()) { Equipment equipment = (Equipment) iter.next(); if (equipment instanceof Chassis) { System.out.println("在柜子里面发现一个盘盒,计算它的价格"); } else if (equipment instanceof Disk) { System.out.println("在盘盒里面发现一个硬盘,计算它的价格"); System.out.println("硬盘本身价格为:" + Disk.diskNetPrice); } netPrice += equipment.netPrice(); } return netPrice; } public double discountPrice() { double discountPrice = 0.; Iterator<Equipment> iter = equipment.iterator(); while (iter.hasNext()) { discountPrice += ((Equipment) iter.next()).discountPrice(); } return discountPrice; } // 这里提供用于访问自己组合体内的部件方法。 // 上面Disk之所以没有,是因为Disk是个单独(Primitive)的元素. public Iterator iter() { return equipment.iterator(); } // 重载Iterator方法 public boolean hasNext() { return i < equipment.size(); } // 重载Iterator方法 public Object next() { if (hasNext()) return equipment.get(i++); else throw new NoSuchElementException(); } }
上面CompositeEquipment继承了Equipment,同时为自己里面的对象们提供了外部访问的方法,重载了Iterator,Iterator是Java的Collection的一个接口,是Iterator模式的实现.
Step 5:
我们在继续创建CompositeEquipment的两个具体类:盘盒Chassis和箱子Cabinet,箱子里面可以放很多东西,如底板,电源盒,硬盘盒等;盘盒里面可以放一些小设备,如硬盘 软驱等。无疑这两个都是属于组合体性质的。
public class Cabinet extends CompositeEquipment { public static double cabinetNetPrice = 10.0; public static double cabinetDiscountPrice = 5.0; public Cabinet(String name) { super(name); } // 柜子本身价格以及放在柜子里面盒子的价格. public double netPrice() { return cabinetNetPrice + super.netPrice(); } public double discountPrice() { return cabinetDiscountPrice + super.discountPrice(); } } public class Chassis extends CompositeEquipment { public static double chassisNetPrice = 2.0; public static double chassisDiscountPrice = 1.0; public Chassis(String name) { super(name); } // 盒子的价格以及盒子里面硬盘价格. public double netPrice() { return chassisNetPrice + super.netPrice(); } // public double discountPrice() { return chassisDiscountPrice + super.discountPrice(); } }
至此我们完成了整个Composite模式的架构。
Step 6:
现在我们来看一下组合模式的使用场景:
public class Client { public static void main(String[] args) { Cabinet cabinet = new Cabinet("柜子"); Chassis chassis = new Chassis("盘盒"); // 将盘盒装到箱子里 cabinet.add(chassis); // 将硬盘装到盘盒里 chassis.add(new Disk("硬盘")); // 查询整个柜子的实体价格 System.out.println("整个柜子的实体价格(包括里面的盘盒和硬盘) =" + cabinet.netPrice()); // 查询整个柜子的折扣价格 System.out.println("整个柜子的折扣价格(包括里面的盘盒和硬盘) =" + cabinet.discountPrice()); } }
上面调用的方法netPrice()或discountPrice(),实际上Composite使用Iterator遍历了整个树形结构,寻找同样包含这个方法的对象并实现调用执行.
Composite是个很巧妙体现智慧的模式,在实际应用中,如果碰到树形结构,我们就可以尝试是否可以使用这个模式。