.NET实用设计模式:组合模式(Composite)
现在我们看一下这个程序需要达到的目标,程序运行后显示下面的界面:
界面上有一个树图,树上显示某公司的人员组织结构,点击这些雇员,会在下面出现这个人的工资。现在程序中有两棵树:一棵是画面上实际的树,另一个是公司中雇员的虚拟的树。画面上的树节点是TreeNode类型,雇员的虚拟树节点是AbstractEmployee类型。我们可以采用组合模式,创造一种新的“节点”,组合这两种节点的特性,简化窗体类需要处理的情况,请看下面的代码:
界面上有一个树图,树上显示某公司的人员组织结构,点击这些雇员,会在下面出现这个人的工资。现在程序中有两棵树:一棵是画面上实际的树,另一个是公司中雇员的虚拟的树。画面上的树节点是TreeNode类型,雇员的虚拟树节点是AbstractEmployee类型。我们可以采用组合模式,创造一种新的“节点”,组合这两种节点的特性,简化窗体类需要处理的情况,请看下面的代码:
EmpNode类是TreeNode类的子类,他具有TreeNode类的所有特性,同时他也组合了AbstractEmployee类型的特点。这样以来调用者的工作就简化了。下面是Form类的代码片断,我把自动生成的代码省略了一部分:public class EmpNode:TreeNode { private AbstractEmployee emp; public EmpNode(AbstractEmployee aemp ):base(aemp.getName ()) { emp = aemp; } //----- public AbstractEmployee getEmployee() { return emp; } }
public class Form1 : System.Windows.Forms.Form { private System.Windows.Forms.Label lbSalary; /**//// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.Container components = null; AbstractEmployee prez, marketVP, salesMgr; TreeNode rootNode; AbstractEmployee advMgr, emp, prodVP, prodMgr, shipMgr; private System.Windows.Forms.TreeView EmpTree; private Random rand; private void init() { rand = new Random (); buildEmployeeList(); buildTree(); } //--------------- 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); } //------ private void getNodeSum(EmpNode node) { AbstractEmployee emp; float sum; emp = node.getEmployee(); sum = emp.getSalaries(); lbSalary.Text = sum.ToString (); } //------ private void addNodes(EmpNode nod, AbstractEmployee emp) { AbstractEmployee newEmp; EmpNode newNode; IEnumerator empEnum; empEnum = emp.getSubordinates(); while (empEnum.MoveNext()) { newEmp = (AbstractEmployee)empEnum.Current; newNode = new EmpNode(newEmp); nod.Nodes.Add(newNode); addNodes(newNode, newEmp); } } //------ private float rand_sal(float sal) { float rnum = rand.Next (); rnum = rnum / Int32.MaxValue; return rnum * sal / 5 + sal; } //------ public Form1() { // // Required for Windows Form Designer support // InitializeComponent(); init(); // // TODO: Add any constructor code after InitializeComponent call // } /**//// <summary> /// Clean up any resources being used. /// </summary> protected override void Dispose( bool disposing ) { if( disposing ) { if (components != null) { components.Dispose(); } } base.Dispose( disposing ); } /**//// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { Application.Run(new Form1()); } private void EmpTree_AfterSelect(object sender, TreeViewEventArgs e) { EmpNode node; node = (EmpNode)EmpTree.SelectedNode; getNodeSum(node); } }
EmpTree_AfterSelect方法是树图点击节点事件的响应方法,用户点击节点后在文本栏里显示相应的工资。组合模式已经介
绍完了,下面的东西和组合模式没有什么关系。
为什么用interface
为什么要在程序中创建雇员的interface呢?我们可以创建一个class Employee,再派生出Boss,一样可以实现上面的功能嘛。
使用interface是为了将画面上的显示程序与后台的业务数据程序分离开。画面的显示程序只需要关心“雇员”提供哪些接口就可以工作了,而不去过问具体的细节,比如工资的计算规则。如果需要对界面类和数据类分别进行单元测试,这样的做法也提供了可能(也就是说,这个程序是可测试的)。测试画面的时候可以在雇员接口上实现一些虚假的雇员类,其中的方法和属性都是为了测试而假造的,这样就可以测试界面的显示是否正确。一般说来程序如果要进行单元测试,应该从设计阶段就考虑程序的“可测试性”,其中重要的一点是:将界面表示与业务逻辑分离开。
0
相关文章