组合模式可以构建部分-整体层次结构或构建数据的树形表示。总之,组合就是对象的集合,其中每个对象既可以是一个组合,也可以是简单的对象。对于树结构来说,有分支节点和叶子节点之说。
总体上来说合成模式应该说是一种应用范围非常广的模式,最常见的莫过于树视图了,每个树视图的节点都可以添加子节点,不断重复就可以构成非常复杂的系统,常用的应用有菜单系统,文件系统都是基于树状结构的。Composite Pattern可分为安全和透明两种设计方式。
透明模式:
在Component抽象角色里面声明所有的用来管理子类对象的方法,包括add、remove,以及getSubordinates方法。这样做的好处是所有的构件类都有相同的接口。在客户端看来,树叶类对象与合成类对象的区别起码在接口层次上消失了,客户端可以同等同的对待所有的对象。这就是透明形式的合成模式。
这个选择的缺点是不够安全,因为树叶类对象和合成类对象在本质上是有区别的。树叶类对象不可能有下一个层次的对象,因此add、remove以及getSubordinates方法没有意义,是在编译时期不会出错,而只会在运行时期才会出错。所以在我们的例子中叶子节点的add,remove方法没有干任何的事情,只是简单的throw new Exception。
以上分为3个角色:
· 抽象构件(Component)角色:这是一个抽象类也可以为接口,它给参与组合的对象规定一个接口。这个角色给出共有接口及其默认行为。
· 树叶构件(Leaf)角色:代表参加组合的树叶对象。一个树叶对象没有下级子对象。
· 树枝构件(Composite)角色:代表参加组合的有子对象的对象,并给出树枝构件对象的行为。
由上图可知Composite对象可以包含其它Component对象。也就是说Composite类型对象可以含有其它的树枝(Composite)对象或树叶(Leaf)对象,这是一棵树的基本特征。
看看代码:
using System;2
using System.Collections;3
using Microsoft.Web.UI.WebControls;4
namespace CompositeTree5
{6
/// <summary>7
///============== Program Description==============8
///Name:CompositeTree.cs9
///Objective:Composite 10
///Date:2006-05-12 11
///Written By coffee.liu12
///================================================13
/// </summary>14
15
// "Component"16
public abstract class Component17
{18
protected string name;19
public Component( string name )20
{ this.name = name; }21
public string getName()22
{ return name;23
}24
abstract public void Add(Component c);25
abstract public void Remove( Component c );26
abstract public IEnumerator getSubordinates();27
}28

29
// "Composite"30
public class Composite : Component31
{32
private ArrayList children = new ArrayList();33
public Composite( string name ) : base( name ) {}34
public override void Add( Component component )35
{ children.Add( component ); }36
37
public override void Remove( Component component )38
{ children.Remove( component ); }39
40
public override IEnumerator getSubordinates()41
{42
return children.GetEnumerator ();43
}44
}45
46
// "Leaf"47
public class Leaf : Component48
{49
private ArrayList childLeaf = new ArrayList(1);50
// Constructors51
public Leaf( string name ) : base( name ) {}52
53
// Methods54
public override void Add( Component c )55
{ throw new Exception("not added!"); }56
57
public override void Remove( Component c )58
{ throw new Exception("not removed!"); }59
public override IEnumerator getSubordinates()60
{61
return childLeaf.GetEnumerator();62
}63
}64
65
public class DisplayNode:TreeNode 66
{67
private Component cmpp;68
public DisplayNode(Component cmp )69
{70
cmp.getName ();71
cmpp = cmp; 72
this.Text=cmp.getName ();73
}74

75
}76

77
}78

再看看页面代码
using System;2
using System.Collections;3
using System.ComponentModel;4
using System.Data;5
using System.Drawing;6
using System.Web;7
using System.Web.SessionState;8
using System.Web.UI;9
using System.Web.UI.WebControls;10
using System.Web.UI.HtmlControls;11

12
namespace CompositeTree13
{14
/// <summary>15
///============== Program Description==============16
///Name:WebForm1.aspx.cs17
///Objective:Composite 18
///Date:2006-05-12 19
///Written By coffee.liu20
///================================================21
/// </summary>22
public class WebForm1 : System.Web.UI.Page23
{24
protected Microsoft.Web.UI.WebControls.TreeView TreeView1;25
Component prez,pxi1,pxi2,pjy1,pjy2,pjy3,pjy4,pjs1,pjs2,pjs3,pjs4,pjs5,pjs6,pjs7;26
private void buildDisplayList() 27
{28
prez = new Composite("南京铁道职业技术学院");29
pxi1=new Composite("通信工程系");30
pxi2=new Composite("计算机系");31
prez.Add(pxi1);32
prez.Add(pxi2);33
pjy1=new Composite("网络教研室");34
pjy2=new Composite("通讯教研室");35
pjy3=new Composite("移动通信教研室");36
pxi1.Add(pjy1);37
pxi1.Add(pjy2);38
pxi1.Add(pjy3);39
pjy4=new Composite("软件");40
pxi2.Add(pjy4);41
pjs1=new Leaf("沈瑞琴");42
pjy1.Add(pjs1);43
pjs2=new Leaf("晏荣");44
pjy1.Add(pjs2);45
pjs3=new Leaf("蒋明华");46
pjy1.Add(pjs3);47
pjs4=new Leaf("赵丽花");48
pjy1.Add(pjs4);49
pjs5=new Leaf("康瑞峰");50
pjy1.Add(pjs5);51
pjs6=new Leaf("冯明兵");52
pjy1.Add(pjs6);53
pjs7=new Leaf("刘伟");54
pjy1.Add(pjs7);55
56
}57
private void buildTree() 58
{59
DisplayNode nod;60

61
nod = new DisplayNode(prez);62
TreeView1.Nodes.Add(nod);63
addNodes(nod, prez);64
}65
private void addNodes(DisplayNode nod, Component cmp) 66
{67
Component newCmp;68
DisplayNode newNode;69
IEnumerator cmpEnum;70
cmpEnum = cmp.getSubordinates();71

72
while (cmpEnum.MoveNext()) 73
{74
newCmp = (Component)cmpEnum.Current;75
newNode = new DisplayNode(newCmp);76
nod.Nodes.Add(newNode);77
addNodes(newNode, newCmp);78
}79
}80
private void Page_Load(object sender, System.EventArgs e)81
{82
83
buildDisplayList();84
buildTree();85

86
}87

88
Web 窗体设计器生成的代码109

110
111
}112
}113

生成的页面如图:
再看看Pasal的树结构代码:
unit UnitCompositeTree;2
//============== Program Description==============3
//Name:CompositeTree.dpr4
//Objective:CompositeTree5
//Date:2006-05-156
//Written By coffee.liu7
//================================================8
interface9

10
uses11
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,12
Dialogs, StdCtrls, ComCtrls;13
// "Component"14
type15
Component= class(TComponent)16
protected name:string;17
public18
function getName():string;19
procedure Add(c:Component);virtual;abstract;20
procedure Remove( c:Component );virtual;abstract;21
function getSubordinates():TList;virtual;abstract;22
constructor create(aname:string);23
destructor Destroy();override;24
end;25
// "Composite"26
type27
Composite= class(Component)28

29
private children:TList;30
public31
constructor create(aname:string);32
procedure Add(acomponent:Component);override;33
procedure Remove(acomponent:Component);override;34
function getSubordinates():TList;override;35
destructor Destroy();override;36
end;37
type38
Leaf=class(Component)39
private childLeaf:TList;40
public41
constructor create(aname:string);42
procedure Add(acomponent:Component);override;43
procedure Remove(acomponent:Component);override;44
function getSubordinates():TList;override;45
destructor Destroy();override;46
end;47
type48
DisplayNode= class(TTreeNode)49
private cmpp:Component;50
public51
constructor create(tr:TTreeNodes;cmp:Component);52
procedure setcomp(cmp:Component);53
destructor Destroy();override;54
55
end;56

57
type EErrorMethod=class(Exception);//定制异常58
type59
TForm1 = class(TForm)60
TreeView1: TTreeView;61
procedure FormCreate(Sender: TObject);62
procedure FormClose(Sender: TObject; var Action: TCloseAction);63
private64
{ Private declarations }65
public66
procedure buildDisplayList(); 67
procedure buildTree();68
procedure addNodesL(anod:TTreeNode;acmp:Component);69
end;70
PTreeViewItem = ^DisplayNode;71
var72
Form1: TForm1;73
prez,pxi1,pxi2,pjy1,pjy2,pjy3,pjy4,pjs1,pjs2,pjs3,pjs4,pjs5,pjs6,pjs7:Component;74
nod:DisplayNode;75
MyTreeNode,MyTreeNode1: TTreeNode;76
implementation77

78
{$R *.dfm}79

80
{ Component }81

82
constructor Component.create(aname: string);83
begin84
name:=aname;85
Form1.InsertComponent(self);86
end;87

88
destructor Component.Destroy;89
begin90

91
inherited;92
end;93

94
function Component.getName: string;95
begin96
result:=name;97
end;98

99
{ Composite }100

101
procedure Composite.Add(acomponent: Component);102
begin103
children.Add(acomponent);104

105
end;106

107
constructor Composite.create(aname: string);108
begin109
inherited;110
children:=TList.Create;111
end;112

113
destructor Composite.Destroy;114
begin115
if Assigned(children) then children.Free;116
inherited;117
end;118

119
function Composite.getSubordinates: TList;120
begin121
result:=children;122
end;123

124

125

126
procedure Composite.Remove(acomponent: Component);127
begin128
children.Remove(acomponent);129
end;130

131
{ Leaf }132

133
procedure Leaf.Add(acomponent: Component);134
begin135
raise EErrorMethod.Create('Leaf not been added!');136
end;137

138
constructor Leaf.create(aname: string);139
begin140
inherited;141
childLeaf:=TList.Create;142
end;143

144
destructor Leaf.Destroy;145
begin146
if Assigned(childLeaf) then childLeaf.Free;147
inherited;148
end;149

150
function Leaf.getSubordinates: TList;151
begin152
result:=childLeaf;153
end;154

155

156

157
procedure Leaf.Remove(acomponent: Component);158
begin159
raise EErrorMethod.Create('Leaf not been removed!');160
end;161

162
{ DisplayNode }163

164
constructor DisplayNode.create(tr:TTreeNodes;cmp: Component);165
begin166
inherited create(tr);167
cmpp:=cmp;168
self.Text:=cmpp.getName;169
end;170

171
destructor DisplayNode.Destroy;172
begin173
if Assigned(cmpp)then cmpp.Free;174
inherited;175
end;176

177
procedure DisplayNode.setcomp(cmp: Component);178
begin179
cmpp:=cmp;180
self.Text:=cmpp.getName;181
end;182

183
{ TForm1 }184

185
procedure TForm1.buildDisplayList;186
begin187
prez := Composite.create('南京铁道职业技术学院');188
pxi1:=Composite.create('通信工程系');189
pxi2:=Composite.create('计算机系');190
prez.Add(pxi1);191
prez.Add(pxi2);192
pjy1:=Composite.create('网络教研室');193
pjy2:=Composite.create('通讯教研室');194
pjy3:=Composite.create('移动通信教研室');195
pxi1.Add(pjy1);196
pxi1.Add(pjy2);197
pxi1.Add(pjy3);198
pjy4:=Composite.create('软件');199
pxi2.Add(pjy4);200
pjs1:=Leaf.create('沈瑞琴');201
pjy1.Add(pjs1);202
pjs2:=Leaf.create('晏荣');203
pjy1.Add(pjs2);204
pjs3:=Leaf.create('蒋明华');205
pjy1.Add(pjs3);206
pjs4:=Leaf.create('赵丽花');207
pjy1.Add(pjs4);208
pjs5:=Leaf.create('康瑞峰');209
pjy1.Add(pjs5);210
pjs6:=Leaf.create('冯明兵');211
pjy1.Add(pjs6);212
pjs7:=Leaf.create('刘伟');213
pjy1.Add(pjs7);214

215
end;216

217
procedure TForm1.buildTree;218
begin219
TreeView1.Items.Clear;220
MyTreeNode:=TreeView1.Items.Add(nil,prez.getName);221
addNodesL(MyTreeNode, prez);222
end;223

224
procedure TForm1.addNodesL(anod: TTreeNode; acmp: Component);225
var226
i:integer;227
newCmp:Component ;228
cmpEnum:TList;229
begin230
cmpEnum := acmp.getSubordinates();231
for i:=0 to cmpEnum.Count-1 do232
begin233
newCmp := Component(cmpEnum[i]);234
MyTreeNode:=TreeView1.Items.AddChild(anod,newCmp.getName);235
addNodesL(MyTreeNode, newCmp);236
end;237
end;238

239
procedure TForm1.FormCreate(Sender: TObject);240
begin241
buildDisplayList;242
buildTree;243
end;244
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);245
begin246
Application.Terminate;247
end;248

249
end.250

运行效果:
2.安全模式
第二种选择是在Composite类里面声明所有的用来管理子类对象的方法。这样的做法是安全的做法,因为树叶类型的对象根本就没有管理子类对象的方法,因此,如果客户端对树叶类对象使用这些方法时,程序会在编译时期出错。这个选择的缺点是不够透明,因为树叶类和合成类将具有不同的接口。

由图可以看出Leaf(leafSafe)去掉了容易引起错误的Add,Remove方法。
· 抽象构件(Component)角色:这是一个抽象角色,其定义出统一接口行为但它并不定义出管理子对象的方法如Add、Remove方法,这一定义由树枝构件对象给出。
· 树叶构件(Leaf)角色:树叶对象没有子对象,它只是定义出参加组合的原始对象的行为。
· 树枝构件(Composite)角色:代表参加组合的有下级子对象的对象。树枝对象给出所有的管理子对象的方法,如add、remove等。
在上面例子的基础上代码修改也十分的容易:
using System;2
using System.Collections;3
using Microsoft.Web.UI.WebControls;4

5
namespace CompositeTree6
{7
/// <summary>8
///============== Program Description==============9
///Name:CompositeTree1.cs10
///Objective:ComponentSafe 11
///Date:2006-05-1412
///Written By coffee.liu13
///================================================14
/// </summary>15
/// // "Component"16
public abstract class ComponentSafe17
{18
protected string name;19
public ComponentSafe( string name )20
{ this.name = name; }21
public string getName()22
{23
return name;24
}25
abstract public IEnumerator getSubordinates();26
27
}28

29
// "Composite"30
public class CompositeSafe : ComponentSafe31
{32
private ArrayList children = new ArrayList();33
public CompositeSafe( string name ) : base( name ) {}34
public void Add( ComponentSafe component )35
{ children.Add( component ); }36
37
public void Remove( ComponentSafe component )38
{ children.Remove( component ); }39
40
public override IEnumerator getSubordinates()41
{42
return children.GetEnumerator ();43
}44
}45
46
// "Leaf"47
public class LeafSafe : ComponentSafe48
{49
private ArrayList childLeaf = new ArrayList(1);50
// Constructors51
public LeafSafe( string name ) : base( name ) {}52
public override IEnumerator getSubordinates()53
{54
return childLeaf.GetEnumerator();55
}56
}57
58
public class DisplayNodeSafe:TreeNode 59
{60
private ComponentSafe cmpp;61
public DisplayNodeSafe(ComponentSafe cmp )62
{63
cmp.getName ();64
cmpp = cmp; 65
this.Text=cmp.getName ();66
}67

68
}69

70
71
}72

页面代码也只需要修改
//Component prez,pxi1,pxi2,pjy1,pjy2,pjy3,pjy4,pjs1,pjs2,pjs3,pjs4,pjs5,pjs6,pjs7;2
CompositeSafe root;3
private void buildDisplayList() 4
{5
root = new CompositeSafe("南京铁道职业技术学院");6
CompositeSafe rootxi=new CompositeSafe("通信工程系");7
root.Add(rootxi);8
CompositeSafe rootxi1=new CompositeSafe("计算机系");9
root.Add(rootxi1);10
CompositeSafe rootjywl=new CompositeSafe("网络教研室");11
CompositeSafe rootjytx=new CompositeSafe("通讯教研室");12
CompositeSafe rootjyyd=new CompositeSafe("移动通信教研室");13
rootxi.Add(rootjywl);14
rootxi.Add(rootjytx);15
rootxi.Add(rootjyyd);16
17
CompositeSafe rootjyrj=new CompositeSafe("软件");18
rootxi1.Add(rootjyrj);19
20
rootjywl.Add(new LeafSafe("沈瑞琴"));21
rootjywl.Add(new LeafSafe("晏荣"));22
rootjywl.Add(new LeafSafe("蒋明华"));23
rootjywl.Add(new LeafSafe("赵丽花"));24
rootjywl.Add(new LeafSafe("康瑞峰"));25
rootjywl.Add(new LeafSafe("冯明兵"));26
rootjywl.Add(new LeafSafe("刘伟"));27
28
29
}即可。同理上面的Pascal代码按照这一思路修改也可。


浙公网安备 33010602011771号