您的位置:首页 > 编程语言 > C#

C#设计模式之9——组合模式

2012-02-24 17:14 375 查看
一般情况下,组件可以是单独的一个对象,也可以是对象的组合,组合模式就是为了迎合这两种情况进行设计。组合可以被用来构建部分-整体层次结构,或者是构造树形的数据表示方式。组合是对象的集合,而其中的任何一个对象又可能是一个组合,或者是一个简单的对象。

在树形结构中,访问组合中所有的对象要求有一个简单的单一访问接口,但同时要求能够区分开节点和叶子。在构造组合的时候,我们需要决定哪个节点是元素哪个是叶子。在这里我们可以通过子节点个数进行判断是不是叶子节点。

 

比如构造一个公司的人事管理系统,经理下面有部门经理,部门经理下面有小组负责人,小组负责人下面有员工这样一个树形结构。在程序里我们需要显示每个人的成本,对于普通员工,一个人的成本就是他的工资,而对于部门经理,他的成本就是他的工资加上手下所有人的工资。

 

我们需要定义两种类:雇员类和老板类,雇员类是叶子节点,老板类下面可以包含子节点。

用户界面如图:



一个类共同的接口:

using System;
using System.Collections ;
namespace Composite
{
/// <summary>
/// Summary description for AbstractEmployee.
/// </summary>
public interface AbstractEmployee  	{
float getSalary();					//get current salary
string getName();					//get name
bool isLeaf();						//true if leaf
void add(string nm, float salary);	//add subordinate
void add(AbstractEmployee emp);		//add subordinate
IEnumerator getSubordinates();		//get subordinates
AbstractEmployee getChild();		//get child
float getSalaries();				//get salaries of all
}
}


 

雇员类的定义:

using System;
using System.Collections ;
namespace Composite
{
/// <summary>
/// Summary description for Employee.
/// </summary>
public class Employee :AbstractEmployee 	{
protected float salary;
protected string name;
protected ArrayList subordinates;
//------
public Employee(string nm, float salry) 		{
subordinates = new ArrayList();
name = nm;
salary = salry;
}
//------
public float getSalary() {
return salary;
}
//------
public string getName() {
return name;
}
//------
public bool isLeaf() {
return subordinates.Count == 0;
}
//------
public virtual void add(string nm, float salary) {
throw new Exception("No subordinates in base employee class");
}
//------
public virtual void add(AbstractEmployee emp) {
throw new Exception("No subordinates in base employee class");
}
//------
public IEnumerator getSubordinates() {
return subordinates.GetEnumerator ();
}
public virtual AbstractEmployee getChild() {
return null;
}
//------
public float getSalaries() {
float sum;
AbstractEmployee esub;
//get the salaries of the boss and subordinates
sum = getSalary();
IEnumerator enumSub = subordinates.GetEnumerator() ;
while (enumSub.MoveNext())  {
esub = (AbstractEmployee)enumSub.Current;
sum += esub.getSalaries();
}
return sum;
}
}
}


 

这里通过IEnumerator对这个节点的子节点进行遍历。

getSalaries() 函数是一个递归的调用,可以计算自身工资和所有子节点工资的总和。

Boss类的定义如下:

using System;
using System.Collections ;
namespace Composite
{
/// <summary>
/// Summary description for Boss.
/// </summary>
public class Boss:Employee
{
public Boss(string name, float salary):base(name,salary) 	{
}
//------
public Boss(AbstractEmployee emp):base(emp.getName() , emp.getSalary()) 	{
}
//------
public override void add(string nm, float salary) {
AbstractEmployee emp = new Employee(nm,salary);
subordinates.Add (emp);
}
//------
public override void add(AbstractEmployee emp){
subordinates.Add(emp);
}
//------
public override AbstractEmployee getChild() {
bool found;
AbstractEmployee tEmp = null;
IEnumerator esub ;

if (getName().Equals (getName()))
return this;
else {
found = false;
esub = subordinates.GetEnumerator ();
while (! found && esub.MoveNext()) {
tEmp = (AbstractEmployee)esub.Current;
found = (tEmp.getName().Equals(name));
if (! found) {
if (! tEmp.isLeaf()) {
tEmp = tEmp.getChild();
found = (tEmp.getName().Equals(name));
}
}
}
if (found)
return tEmp;
else
return new Employee("New person", 0);
}
}
}
}


Boss类继承了Employee类,因为Boss类本身也是雇员。

 

在主程序中就可以构建雇员的树形结构了:

private void buildEmployeeList() {
prez = new Boss("CEO", 200000);
marketVP = new Boss("Marketing VP", 100000);
prez.add(marketVP);
salesMgr = new Boss("Sales Mgr", 50000);
advMgr = new Boss("Advt Mgr", 50000);
marketVP.add(salesMgr);
marketVP.add(advMgr);
prodVP = new Boss("Production VP", 100000);
prez.add(prodVP);
advMgr.add("Secy", 20000);

//add salesmen reporting to sales manager
for (int i = 1; i<=5; i++){
salesMgr.add("Sales" + i.ToString(), rand_sal(30000));
}

prodMgr = new Boss("Prod Mgr", 40000);
shipMgr = new Boss("Ship Mgr", 35000);
prodVP.add(prodMgr);
prodVP.add(shipMgr);

for (int i = 1; i<=3; i++){
shipMgr.add("Ship" + i.ToString(), rand_sal(25000));
}
for (int i = 1; i<=4; i++){
prodMgr.add("Manuf" + i.ToString(), rand_sal(20000));
}
}
//-----
private void buildTree() {
EmpNode nod;

nod = new EmpNode(prez);
rootNode = nod;
EmpTree.Nodes.Add(nod);
addNodes(nod, prez);
}


 

using System;
using System.Windows.Forms;
namespace Composite
{
/// <summary>
/// Summary description for EmpNode.
/// </summary>
public class EmpNode:TreeNode 	{
private AbstractEmployee emp;
public EmpNode(AbstractEmployee aemp ):base(aemp.getName ()) {
emp = aemp;
}
//-----
public AbstractEmployee getEmployee() {
return emp;
}
}
}


这样就完成一个组合模式的对对象的封装。

然后还需要下面一些时间函数:

private void EmpTree_AfterSelect(object sender, TreeViewEventArgs e) {
EmpNode node;
node = (EmpNode)EmpTree.SelectedNode;
getNodeSum(node);
}


 

private void getNodeSum(EmpNode node) {
AbstractEmployee emp;
float sum;

emp = node.getEmployee();
sum = emp.getSalaries();
lbSalary.Text = sum.ToString ();


在这个例子的基础上,我们还可以添加树形结构的双向链表,从而可以进行从叶子节点向上遍历,方便的查找父类信息。

这样对Employee类的改动如下:

using System;
using System.Collections ;
namespace Composite
{
/// <summary>
/// Summary description for Employee.
/// </summary>
public class Employee :AbstractEmployee 	{
protected float salary;
protected string name;
protected AbstractEmployee parent;
protected ArrayList subordinates;
//------
public Employee(AbstractEmployee parnt, string nm, float salry) 		{
subordinates = new ArrayList();
name = nm;
salary = salry;
parent = parnt;
}
//------
public AbstractEmployee getBoss() {
return parent;
}
//------
public float getSalary() {
return salary;
}
//------
public string getName() {
return name;
}
//------
public bool isLeaf() {
return subordinates.Count == 0;
}
//------
public virtual void add(string nm, float salary) {
throw new Exception("No subordinates in base employee class");
}
//------
public virtual void add(AbstractEmployee emp) {
throw new Exception("No subordinates in base employee class");
}
//------
public IEnumerator getSubordinates() {
return subordinates.GetEnumerator ();
}
public virtual AbstractEmployee getChild() {
return null;
}
//------
public float getSalaries() {
float sum;
AbstractEmployee esub;
//get the salaries of the boss and subordinates
sum = getSalary();
IEnumerator enumSub = subordinates.GetEnumerator() ;
while (enumSub.MoveNext())  {
esub = (AbstractEmployee)enumSub.Current;
sum += esub.getSalaries();
}
return sum;
}
}
}


 

这个类中添加了一个指向父类的引用。

这样就可以向上遍历,找到上级领导信息:

private void btShowBoss_Click(object sender, System.EventArgs e) {
EmpNode node;
node = (EmpNode)EmpTree.SelectedNode;
AbstractEmployee emp = node.getEmployee ();
string bosses = "";
while(emp != null) {
bosses += emp.getName () +"\n";
emp = emp.getBoss();
}
MessageBox.Show (null, bosses,"Reporting chain");
}


组合模式允许定义简单对象和更加复杂的组件对象的类层次结构,这样这些对象就能够以相同的方式呈献给客户端程序。基于这一简易性,客户端就可以更加价单,因为节点和叶子可以被以同样的方式处理。

组合模式还可以让你很容易的添加新类型的组件到集合中,只要这些组件支持相类似的编程接口就可以了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息