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

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

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

 

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

 

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

用户界面如图:

一个类共同的接口:

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");
	}


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

组合模式还可以让你很容易的添加新类型的组件到集合中,只要这些组件支持相类似的编程接口就可以了。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值