合成模型模式(Composite)属于队形的结构模式,有时又叫做部分-整体模式(Part-Whole)。
合成模式将对象组织到树结构中,可以用来描述整体与部分的关系。
合成模式可以使客户端将单纯元素与复合元素同等看待。
树
有向树结构的种类:
- 从上向下
- 从下向上
- 双向的
这里仅给出由上向下的示意图
一个树结构由两种节点组成:树枝节点和树叶节点。树枝节点可以有子节点,而一个树叶节点不可以有子节点。
在信息系统里,树枝节点所代表的构件常常用作树叶节点所代表的构件的容器。
合成模式
合成模式把部分和整体的关系用树结构表示出来。
从这个简略类图,可以看出,涉及三个角色:
- 抽象构件角色(Component),
- 树叶构件角色(Leaf),
- 树枝构件角色(Composite)
分成两类:
- 安全式的合成模式
- 透明式的合成模式
透明方式,
会在Component里面声明所有的用来管理子类对象的方法,包括 add(), remove() , 以及 getChild() 方法。
这样做的好处是所有的构件类都有相同的接口。
客户端可以同等对待所有的对象。
缺点是不够安全,因为树叶类对象和合成对象在本质上是由区别的。
安全方法
安全方式,是在Composite 类里面声明所有的用来管理子类对象的方法。
这样的做法是安全的做法,因为树叶类型的对象根本就没有管理子类对象的方法,
因此,如果客户端对树叶类对象使用这些方法时,程序会在编译时期出错。
缺点是,不够透明,因为树叶类和合成类将具有不同的接口。
安全式的合成模式
- 抽象构件角色(Component):这是一个抽象角色,它给参加组合的对象定义出公共的接口及其默认行为,可以用来管理所有的子对象。在安全式的合成模式里,构件角色并不定义出管理子对象的方法,这一定义由树枝构件对象给出。
- 树叶构件角色(Leaf): 树叶对象是没有下级子对象的对象,定义出参加组合的原始对象的行为。
- 树枝构件角色(Composite): 代表参加组合的有下级子对象的对象。树枝构件类给出所有的管理子对象的方法,如 add(), remove() 以及 components()的声明。
代码清单:
Component
public interface Component { /** * 返还自己的实例 * @return */ Composite getComposite(); /** * 某个商业方法 */ void sampleOperation(); }
Composite
public class Composite implements Component { private Vector componentVector = new Vector(); /** * 返还自己的实例 */ @Override public Composite getComposite() { // TODO Auto-generated method stub return this; } /** * 某个商业方法 */ @Override public void sampleOperation() { Enumeration enumeration = components(); while(enumeration.hasMoreElements()){ ((Component)enumeration.nextElement()).sampleOperation(); } } /** * 聚集管理方法,增加一个子构件对象 * @param component */ public void add(Component component){ componentVector.addElement(component); } /** * 聚集管理方法,删除一个子构件对象 * @param component */ public void remove(Component component){ componentVector.removeElement(component); } /** * 聚集管理方法,返还聚集的Enumeration对象 * @return */ public Enumeration components(){ return componentVector.elements(); } }
Leaf
public class Leaf implements Component { /** * 返还自己的实例 */ @Override public Composite getComposite() { // TODO Auto-generated method stub return null; } /** * 某个商业方法 */ @Override public void sampleOperation() { // TODO Auto-generated method stub } }
透明式的合成模式的结构
三个角色:
- 抽象构件角色(Component): 这是一个抽象角色,它给参加组合的对象规定一个接口,规范共有的接口及默认行为。这个接口可以用来管理所有的子对象,要提供一个接口以规范取得和管理下层组件的接口,包括 add() , remove() 以及 getChild()之类的方法
- 树叶构件角色(Leaf): 代表参加组合的树叶对象,定义出参加组合的原始对象的行为。树叶对象会给出 add() , remove() 以及 getChild()之类的用来管理子类对象的方法的平庸实现。
- 树枝构件角色(Composite): 代表参加组合的有子对象的对象,定义出这样对象的行为。
代码清单:
抽象构件角色 Component 接口
package anquanhecheng; import java.util.Enumeration; public interface Component { /** * 返还自己的实例 * @return */ Composite getComposite(); /** * 某个商业方法 */ void sampleOperation(); /** * 聚集管理方法,增加一个子构件对象 * @param component */ void add(Component component); /** * 聚集管理方法,删除一个子构件对象 * @param component */ void remove(Component component); /** * 聚集管理方法,返还聚集的Enumeration对象 * @return */ Enumeration components(); }
树枝构件类(Composite)
package anquanhecheng; import java.util.Enumeration; import java.util.Vector; public class Composite implements Component { private Vector componentVector = new Vector(); /** * 返还自己的实例 */ @Override public Composite getComposite() { // TODO Auto-generated method stub return this; } /** * 某个商业方法 */ @Override public void sampleOperation() { Enumeration enumeration = components(); while(enumeration.hasMoreElements()){ ((Component)enumeration.nextElement()).sampleOperation(); } } /** * 聚集管理方法,增加一个子构件对象 * @param component */ public void add(Component component){ componentVector.addElement(component); } /** * 聚集管理方法,删除一个子构件对象 * @param component */ public void remove(Component component){ componentVector.removeElement(component); } /** * 聚集管理方法,返还聚集的Enumeration对象 * @return */ public Enumeration components(){ return componentVector.elements(); } }
树叶构件类(Leaf)
package anquanhecheng; import java.util.Enumeration; public class Leaf implements Component { /** * 返还自己的实例 */ @Override public Composite getComposite() { // TODO Auto-generated method stub return null; } /** * 某个商业方法 */ @Override public void sampleOperation() { // TODO Auto-generated method stub } /** * 聚集管理方法,增加一个子构件对象 * @param component */ @Override public void add(Component component) { // TODO Auto-generated method stub } /** * 聚集管理方法,删除一个子构件对象 * @param component */ @Override public void remove(Component component) { // TODO Auto-generated method stub } /** * 聚集管理方法,返还聚集的Enumeration对象 * @return */ @Override public Enumeration components() { // TODO Auto-generated method stub return null; } }
一个绘图的例子
一个绘图系统给出各种工具用来描绘由线,长方形和圆形灯基本图形组成的图形。
显然,一个复合的图形既然是由这些基本图形组成的,那么合成模式就是一个合适的设计模式。
可以想象,设计应当包括 Line, Rectangle和 Circle等对象,而且每一个对象都应该配备一个draw()方法,在调用时,会画出对象所代表的图形。
同时,由于一个复杂的图形是由基本图形组合而成的,因此,一个合成的图形应当有一个列表,存储对所有的基本图形对象的引用。复合图形的draw() 方法在调用时,应当逐一调用所有列表上的基本图形对象的 draw() 方法。
下面是它的两种实现:
应用安全式合成模式
安全形式的合成模式意味着只有树枝构件角色才配备有管理聚集的方法,而树叶构件则没有这些方法。
抽象构件角色
package hecheng_huitu; /** * 抽象构件角色的实现 * @author Administrator * */ public abstract class Graphics { public abstract void draw(); }
树枝构件角色
public class Picture extends Graphics { private Vector list = new Vector(10); @Override public void draw() { // TODO Auto-generated method stub for(int i=0;i<list.size();i++){ Graphics g = (Graphics)list.get(i); g.draw(); } } /** * 聚集管理方法,增加一个子构件对象 * @param g */ public void add(Graphics g){ list.add(g); } /** * 聚集管理方法,删除一个子构件对象 * @param g */ public void remove(Graphics g){ list.remove(g); } /** * 返还一个子构件对象 * @param g */ public Graphics getChild(int i){ return (Graphics)list.get(i); } }
Line 作为一个树叶构件的实现
public class Line extends Graphics { @Override public void draw() { //write your code here } }
Rectangle作为一个树叶构件
public class Rectangle extends Graphics { @Override public void draw() { // write your code here } }
Circle 作为一个树叶构件
public class Circle extends Graphics { @Override public void draw() { // write your code here } }
应用透明式的合成模式
透明形式的合成模式意味着不仅只有树枝构件角色才配备有管理聚集的方法,树叶构件也有这些方法,虽然树叶构件的这些方法是平庸的。
抽象构件角色 Graphics
public abstract class Graphics { public abstract void draw(); public abstract void add(Graphics g); public abstract void remove(Graphics g); public abstract Graphics getChild(int i); }
树枝构件角色 Picture
public class Picture extends Graphics { private Vector list = new Vector(10); @Override public void draw() { // TODO Auto-generated method stub for(int i=0;i<list.size();i++){ Graphics g = (Graphics)list.get(i); g.draw(); } } /** * 聚集管理方法,增加一个子构件对象 * @param g */ public void add(Graphics g){ list.add(g); } /** * 聚集管理方法,删除一个子构件对象 * @param g */ public void remove(Graphics g){ list.remove(g); } /** * 返还一个子构件对象 * @param g */ public Graphics getChild(int i){ return (Graphics)list.get(i); } }
树叶构件角色 Line
public class Line extends Graphics { @Override public void draw() { //write your code here } @Override public void add(Graphics g) { // TODO Auto-generated method stub } @Override public void remove(Graphics g) { // TODO Auto-generated method stub } @Override public Graphics getChild(int i) { // TODO Auto-generated method stub return null; } }
Rectangle
public class Rectangle extends Graphics { @Override public void draw() { // write your code here } @Override public void add(Graphics g) { // TODO Auto-generated method stub } @Override public void remove(Graphics g) { // TODO Auto-generated method stub } @Override public Graphics getChild(int i) { // TODO Auto-generated method stub return null; } }
Circle
public class Circle extends Graphics { @Override public void draw() { // write your code here } @Override public void add(Graphics g) { // TODO Auto-generated method stub } @Override public void remove(Graphics g) { // TODO Auto-generated method stub } @Override public Graphics getChild(int i) { // TODO Auto-generated method stub return null; } }
AWT库中的例子
在什么情况下应当考虑使用合成模式
- 需要描述对象的部分和整体的等级结构
- 需要客户端忽略掉个体构件和组件构件的区别。客户端必须平等对待所有的构件,包括个体构件和组合构件。
合成模式的优缺点
优点:
- 合成模式可以很容易的增加新种类的构件
- 使用合成模式可以是客户端变得很容易设计,因为客户端不需要知道构件是树叶构件还是树枝构件。
缺点:
- 使用合成模式后,控制树枝构件的类型就不太容易
- 用继承的方法来增加新的行为很困难。