合成(Composite)模式
合成模式
合成(Composite)模型模式属于对象的结构模式,有时又叫部分-整体(Part-Whole)模式。合成模式将对象组织到树结构中,可以描述整体与部分的关系。合成模式可以使客户端将单纯元素与符合元素同等看待。
文件系统
一个文件系统就是一个典型的合成模式系统。下图是Mac下文件系统的一部分。
从上图可以看出文件系统是一个属性结构,树上有节点。树的节点有两种一种是树枝,一种是树叶,图中文件夹即树枝,文件即树叶。
合成模式分两种一种是安全式一种是透明式。
下图的类图省去了各角色的细节:
可以看出类图结构设计三个角色:
- 抽象构件(Component)角色:只是一个抽象角色,它给参加组合的对象规定一个接口。这个角色给出共有的接口及其默认行为。
- 树叶构件( Leaf)角色:代表参加组合的树叶对象,树叶是没有下级对象,定义参加组合的原始对象行为。
- 树枝构件(Composite)角色:代表参加组合的有子对象的对象,并给出树枝对象的行为。
可以看出,Composite类型的对象哈有其它的Component类型的对象。换言之,Composite对象包含其它所有树枝类型的对象或树叶类型的对象。
透明方式
在Component中声明所有用来管理子类对象的方法,包括:add(),remove(),以及getChild(),这样的好处是所有的构件类都有相同的接口,在客户端来看,树叶对象与合成对象的区别起码在接口层上消失了,客户端可以同等的对待所有对象,这就是透明形式的合成模式。
这种方式的缺点就是不够安全,因为树枝对象和树叶对象本质上是有区别的,树叶对象不可能有下级对象,比如add()、Remove()、以及getChild()方法没有意义,但是编译时不会出错,运行时会出错。
安全方式
安全方式是在Composite中声明所有用来管理子类的方法,这样的做法是安全做法,因为树叶类型根本没有管理子类对象的方法,因此,如果客户端对树叶类型对象使用这个方法时,程序会在编译时出错。
安全式的合成模式的结构
安全式的合成模式 要求管理聚集的方法只出现在在树枝构件类中,而不出现在树叶构件类中。如下图:
涉及三个角色,抽象构件角色,树叶构件角色,树枝构件角色,具体作用不在赘述。
代码清单1:抽象构件角色
public interface Component {
Composite getComposite();
void sampleOperation();
}
代码清单2:树叶构件角色
public class Leaf implements Component {
public Composite getComposite(){
// Write your code here
return null;
}
public void sampleOperation(){
// Write your code here
}
}
代码清单3:树枝构件角色
import java.util.Vector;
import java.util.Enumeration;
public class Composite implements Component
{
public Composite getComposite()
{
return this;
}
public void sampleOperation()
{
java.util.Enumeration enumeration = components();
while (enumeration.hasMoreElements())
{
((Component)enumeration.nextElement()).sampleOperation();
}
}
public void add(Component component)
{
componentVector.addElement(component);
}
public void remove(Component component)
{
componentVector.removeElement(component);
}
public Enumeration components()
{
return componentVector.elements();
}
/**
* @associates <{Component}>
* @link aggregation
* @supplierCardinality 0..*
*/
private Vector componentVector = new java.util.Vector();
}
可以看出只有树枝构件给出了add()、remove()、以及component()等方法的声明和实现,而树叶构件类则没有给出这些方法的声明或实现。由于这个特点,客户端程序不可能错误的调用树叶构件的聚集方法,因为树叶构件没有这些方法,导致编译错误。
安全式的合成模式的结构
与安全式的合成模式不同的是,透明式的合成模式要求所有的具体构件类,不论树枝构件还是树叶构件,均符合一个固定接口。
代码清单1:抽象构件角色
public interface Component {
void sampleOperation();
void add(Component component);
void remove(Component component);
Enumeration<?> components();
}
代码清单2:树枝构件角色
public class Composite implements Component {
public Composite getComposite() {
return this;
}
public void sampleOperation() {
java.util.Enumeration enumeration = components();
while (enumeration.hasMoreElements()) {
((Component) enumeration.nextElement()).sampleOperation();
}
}
public void add(Component component) {
componentVector.addElement(component);
}
public void remove(Component component) {
componentVector.removeElement(component);
}
public Enumeration components() {
return componentVector.elements();
}
/**
* @associates <{Component}>
* @link aggregation
* @supplierCardinality 0..*
*/
private Vector componentVector = new java.util.Vector();
}
代码清单3,树叶构件角色
public class Leaf implements Component {
public Composite getComposite() {
return null;
}
public void sampleOperation() {
}
public void add(Component component) {
}
public void remove(Component component) {
}
public Enumeration components() {
return null;
}
/**
* @associates <{Component}>
* @link aggregation
* @supplierCardinality 0..*
*/
private Vector componentVector = new java.util.Vector();
}
可以看出透明合成模式中树叶类给了add()、remove()以及components等方法的平庸实现,因为这些方法不适合用于树叶类。
下面做个安全式合成模式的例子:
/**
* @author: Col.Mai
* @date:2014-9-21 下午7:57:50
* @description :<p>抽象构件角色类</P>
**/
public interface Component {
/**
* 输出组建自身的名称
*/
public void printStruct(String preStr);
}
import java.util.Vector;
/**
* @author: Col.Mai
* @date:2014-9-21 下午7:58:15
* @description :<p>xxx功能描述</P>
**/
public class Composite implements Component {
/**
* 用来存储组合对象中包含的子组件对象
*/
private Vector<Component> childComponents = new Vector<Component>();
/**
* 组合对象的名字
*/
private String name;
/**
* 构造方法,传入组合对象的名字
* @param name 组合对象的名字
*/
public Composite(String name){
this.name = name;
}
/**
* 聚集管理方法,增加一个子构件对象
* @param child 子构件对象
*/
public void addChild(Component child){
childComponents.add(child);
}
/**
* 聚集管理方法,删除一个子构件对象
* @param index 子构件对象的下标
*/
public void removeChild(int index){
childComponents.remove(index);
}
/**
* 聚集管理方法,返回所有子构件对象
*/
public Vector<Component> getChild(){
return childComponents;
}
/**
* 输出对象的自身结构
* @param preStr 前缀,主要是按照层级拼接空格,实现向后缩进
*/
public void printStruct(String preStr) {
// 先把自己输出
System.out.println(preStr + "+" + this.name);
//如果还包含有子组件,那么就输出这些子组件对象
if(this.childComponents != null){
//添加两个空格,表示向后缩进两个空格
preStr += " ";
//输出当前对象的子对象
for(Component c : childComponents){
//递归输出每个子对象
c.printStruct(preStr);
}
}
}
}
/**
* @author: Col.Mai
* @date:2014-9-21 下午7:59:58
* @description :<p>树叶构件角色类</P>
**/
public class Leaf implements Component {
/**
* 叶子对象的名字
*/
private String name;
/**
* 构造方法,传入叶子对象的名称
* @param name 叶子对象的名字
*/
public Leaf(String name){
this.name = name;
}
/**
* 输出叶子对象的结构,叶子对象没有子对象,也就是输出叶子对象的名字
* @param preStr 前缀,主要是按照层级拼接的空格,实现向后缩进
*/
public void printStruct(String preStr) {
System.out.println(preStr + "-" + name);
}
}
/**
* @author: Col.Mai
* @date:2014-9-21 下午8:00:43
* @description :
* <p>
* xxx功能描述
* </P>
**/
public class Client {
public static void main(String[] args) {
Composite root = new Composite("E");
Composite c1 = new Composite("日本浪漫爱情动作片");
Composite c2 = new Composite("美国大片");
Leaf leaf1 = new Leaf("苍井空.AVI");
Leaf leaf2 = new Leaf("小泽玛利亚.AVI");
Leaf leaf3 = new Leaf("阿凡达.mkv");
Leaf leaf4 = new Leaf("钢铁侠.mkv");
root.addChild(c1);
root.addChild(c2);
c1.addChild(leaf1);
c1.addChild(leaf2);
c2.addChild(leaf3);
c2.addChild(leaf4);
root.printStruct("");
}
}
输出如下:
+E
+日本浪漫爱情动作片
-苍井空.AVI
-小泽玛利亚.AVI
+美国大片
-阿凡达.mkv
-钢铁侠.mkv