组合模式可以构建部分-整体层次结构或构建数据的树形表示。总之,组合就是对象的集合,其中每个对象既可以是一个组合,也可以是简单的对象。对于树结构来说,有分支节点和叶子节点之说。
总体上来说合成模式应该说是一种应用范围非常广的模式,最常见的莫过于树视图了,每个树视图的节点都可以添加子节点,不断重复就可以构成非常复杂的系统,常用的应用有菜单系统,文件系统都是基于树状结构的。Composite Pattern可分为安全和透明两种设计方式。
透明模式:
在Component抽象角色里面声明所有的用来管理子类对象的方法,包括add、remove,以及getSubordinates方法。这样做的好处是所有的构件类都有相同的接口。在客户端看来,树叶类对象与合成类对象的区别起码在接口层次上消失了,客户端可以同等同的对待所有的对象。这就是透明形式的合成模式。
这个选择的缺点是不够安全,因为树叶类对象和合成类对象在本质上是有区别的。树叶类对象不可能有下一个层次的对象,因此add、remove以及getSubordinates方法没有意义,是在编译时期不会出错,而只会在运行时期才会出错。所以在我们的例子中叶子节点的add,remove方法没有干任何的事情,只是简单的throw new Exception。
以上分为3个角色:
· 抽象构件(Component)角色:这是一个抽象类也可以为接口,它给参与组合的对象规定一个接口。这个角色给出共有接口及其默认行为。
· 树叶构件(Leaf)角色:代表参加组合的树叶对象。一个树叶对象没有下级子对象。
· 树枝构件(Composite)角色:代表参加组合的有子对象的对象,并给出树枝构件对象的行为。
由上图可知Composite对象可以包含其它Component对象。也就是说Composite类型对象可以含有其它的树枝(Composite)对象或树叶(Leaf)对象,这是一棵树的基本特征。
看看代码:
2using System.Collections;
3using Microsoft.Web.UI.WebControls;
4namespace CompositeTree
5{
6 /// <summary>
7 ///============== Program Description==============
8 ///Name:CompositeTree.cs
9 ///Objective:Composite
10 ///Date:2006-05-12
11 ///Written By coffee.liu
12 ///================================================
13 /// </summary>
14
15 // "Component"
16 public abstract class Component
17 {
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 : Component
31 {
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 : Component
48 {
49 private ArrayList childLeaf = new ArrayList(1);
50 // Constructors
51 public Leaf( string name ) : base( name ) {}
52
53 // Methods
54 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
再看看页面代码
2using System.Collections;
3using System.ComponentModel;
4using System.Data;
5using System.Drawing;
6using System.Web;
7using System.Web.SessionState;
8using System.Web.UI;
9using System.Web.UI.WebControls;
10using System.Web.UI.HtmlControls;
11
12namespace CompositeTree
13{
14 /// <summary>
15 ///============== Program Description==============
16 ///Name:WebForm1.aspx.cs
17 ///Objective:Composite
18 ///Date:2006-05-12
19 ///Written By coffee.liu
20 ///================================================
21 /// </summary>
22 public class WebForm1 : System.Web.UI.Page
23 {
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的树结构代码:
2//============== Program Description==============
3 //Name:CompositeTree.dpr
4 //Objective:CompositeTree
5 //Date:2006-05-15
6 //Written By coffee.liu
7 //================================================
8interface
9
10uses
11 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
12 Dialogs, StdCtrls, ComCtrls;
13 // "Component"
14 type
15 Component= class(TComponent)
16 protected name:string;
17 public
18 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 type
27 Composite= class(Component)
28
29 private children:TList;
30 public
31 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 type
38 Leaf=class(Component)
39 private childLeaf:TList;
40 public
41 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 type
48 DisplayNode= class(TTreeNode)
49 private cmpp:Component;
50 public
51 constructor create(tr:TTreeNodes;cmp:Component);
52 procedure setcomp(cmp:Component);
53 destructor Destroy();override;
54
55 end;
56
57 type EErrorMethod=class(Exception);//定制异常
58type
59 TForm1 = class(TForm)
60 TreeView1: TTreeView;
61 procedure FormCreate(Sender: TObject);
62 procedure FormClose(Sender: TObject; var Action: TCloseAction);
63 private
64 { Private declarations }
65 public
66 procedure buildDisplayList();
67 procedure buildTree();
68 procedure addNodesL(anod:TTreeNode;acmp:Component);
69 end;
70 PTreeViewItem = ^DisplayNode;
71var
72 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;
76implementation
77
78{$R *.dfm}
79
80{ Component }
81
82constructor Component.create(aname: string);
83begin
84 name:=aname;
85 Form1.InsertComponent(self);
86end;
87
88destructor Component.Destroy;
89begin
90
91 inherited;
92end;
93
94function Component.getName: string;
95begin
96 result:=name;
97end;
98
99{ Composite }
100
101procedure Composite.Add(acomponent: Component);
102begin
103 children.Add(acomponent);
104
105end;
106
107constructor Composite.create(aname: string);
108begin
109 inherited;
110 children:=TList.Create;
111end;
112
113destructor Composite.Destroy;
114begin
115 if Assigned(children) then children.Free;
116 inherited;
117end;
118
119function Composite.getSubordinates: TList;
120begin
121 result:=children;
122end;
123
124
125
126procedure Composite.Remove(acomponent: Component);
127begin
128 children.Remove(acomponent);
129end;
130
131{ Leaf }
132
133procedure Leaf.Add(acomponent: Component);
134begin
135raise EErrorMethod.Create('Leaf not been added!');
136end;
137
138constructor Leaf.create(aname: string);
139begin
140 inherited;
141 childLeaf:=TList.Create;
142end;
143
144destructor Leaf.Destroy;
145begin
146 if Assigned(childLeaf) then childLeaf.Free;
147 inherited;
148end;
149
150function Leaf.getSubordinates: TList;
151begin
152 result:=childLeaf;
153end;
154
155
156
157procedure Leaf.Remove(acomponent: Component);
158begin
159 raise EErrorMethod.Create('Leaf not been removed!');
160end;
161
162{ DisplayNode }
163
164constructor DisplayNode.create(tr:TTreeNodes;cmp: Component);
165begin
166 inherited create(tr);
167 cmpp:=cmp;
168 self.Text:=cmpp.getName;
169end;
170
171destructor DisplayNode.Destroy;
172begin
173 if Assigned(cmpp)then cmpp.Free;
174 inherited;
175end;
176
177procedure DisplayNode.setcomp(cmp: Component);
178begin
179 cmpp:=cmp;
180 self.Text:=cmpp.getName;
181end;
182
183{ TForm1 }
184
185procedure TForm1.buildDisplayList;
186begin
187 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
215end;
216
217procedure TForm1.buildTree;
218begin
219 TreeView1.Items.Clear;
220 MyTreeNode:=TreeView1.Items.Add(nil,prez.getName);
221 addNodesL(MyTreeNode, prez);
222end;
223
224procedure TForm1.addNodesL(anod: TTreeNode; acmp: Component);
225var
226i:integer;
227 newCmp:Component ;
228 cmpEnum:TList;
229begin
230 cmpEnum := acmp.getSubordinates();
231 for i:=0 to cmpEnum.Count-1 do
232 begin
233 newCmp := Component(cmpEnum[i]);
234 MyTreeNode:=TreeView1.Items.AddChild(anod,newCmp.getName);
235 addNodesL(MyTreeNode, newCmp);
236 end;
237end;
238
239procedure TForm1.FormCreate(Sender: TObject);
240begin
241 buildDisplayList;
242 buildTree;
243end;
244procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
245begin
246Application.Terminate;
247end;
248
249end.
250
运行效果:
2.安全模式
第二种选择是在Composite类里面声明所有的用来管理子类对象的方法。这样的做法是安全的做法,因为树叶类型的对象根本就没有管理子类对象的方法,因此,如果客户端对树叶类对象使用这些方法时,程序会在编译时期出错。这个选择的缺点是不够透明,因为树叶类和合成类将具有不同的接口。
由图可以看出Leaf(leafSafe)去掉了容易引起错误的Add,Remove方法。
· 抽象构件(Component)角色:这是一个抽象角色,其定义出统一接口行为但它并不定义出管理子对象的方法如Add、Remove方法,这一定义由树枝构件对象给出。
· 树叶构件(Leaf)角色:树叶对象没有子对象,它只是定义出参加组合的原始对象的行为。
· 树枝构件(Composite)角色:代表参加组合的有下级子对象的对象。树枝对象给出所有的管理子对象的方法,如add、remove等。
在上面例子的基础上代码修改也十分的容易:
2using System.Collections;
3using Microsoft.Web.UI.WebControls;
4
5namespace CompositeTree
6{
7 /// <summary>
8 ///============== Program Description==============
9 ///Name:CompositeTree1.cs
10 ///Objective:ComponentSafe
11 ///Date:2006-05-14
12 ///Written By coffee.liu
13 ///================================================
14 /// </summary>
15 /// // "Component"
16 public abstract class ComponentSafe
17 {
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 : ComponentSafe
31 {
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 : ComponentSafe
48 {
49 private ArrayList childLeaf = new ArrayList(1);
50 // Constructors
51 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
页面代码也只需要修改
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代码按照这一思路修改也可。