.NET中的设计模式三:组合模式

设计

组合模式(Composite)是一种“结构型”模式(Structural)。结构型模式涉及的对象为两个或两个以上,表示对象之间的活动,与对象的结构有关。
先举一个组合模式的小小例子:

如图:系统中有两种Box:Game Box和Internet Box,客户需要了解者两个类的接口分别进行调用。为了简化客户的工作,创建了XBox类,程序代码如下:

GameBox的代码:

public class GameBox

{

public void PlayGame()

{

Console.WriteLine("plaly game");

}

}

InternetBox的代码:

public class InternetBox

{

public void ConnectToInternet()

{

Console.WriteLine("connect to internet");

}

public void GetMail()

{

Console.WriteLine("check email");

}

}

XBox的代码:

public class XBox

{

private GameBox mGameBox=null;

private InternetBox mInternetBox=null;

public XBox()

{

mGameBox = new GameBox();

mInternetBox = new InternetBox();

}

public void PlayGame()

{

mGameBox.PlayGame();

}

public void ConnectToInternet()

{

mInternetBox.ConnectToInternet();

}

public void GetMail()

{

mInternetBox.GetMail();

}

}

XBox中封装了GameBox和InternetBox的方法,这样,用户面对的情况就大大的简化了,调用的代码如下:

public class CSComposite

{

static void Main (string[] args)

{

XBox x = new XBox();

Console.WriteLine("PlayGame!");

x.PlayGame();

Console.WriteLine();

Console.WriteLine("Internet Play Game!");

x.ConnectToInternet();

x.PlayGame();

Console.WriteLine();

Console.WriteLine("E-Mail!");

x.GetMail();

}

}

可以看见,用户只需要了解XBox的接口就可以了。

组合模式的应用例子

组合模式适用于下面这样的情况:两个或者多个类有相似的形式,或者共同代表某个完整的概念,外界的用户也希望他们合而为一,就可以把这几个类“组合”起来,成为一个新的类,用户只需要调用这个新的类就可以了。

下面举一个例子说明Composite模式的一个实际应用。下面的Class视图:

Employee类是AbstractEmployee接口的一个实现,Boss类是Employee的一个子类,EmpNode是从树视图的TreeNode类继承而来的。我们先看看代码:

AbstractEmployee,这是一个接口,提供下列方法:

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

}

Employee类是AbstractEmployee接口的一个实现

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;

}

}

从Employee接口和他的一个实现来看,下面很可能要将这个类型的数据组合成一个树的结构。

Boss类是Employee类的派生,他重载了Employee类的add和getChild方法:

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

}

}

}

getChild方法是一个递归调用,如果Child不是Leaf,就继续调用下去。上面几个类表达了一个树的结构,表示出了公司中的领导和雇员的级别关系。

现在我们看一下这个程序需要达到的目标,程序运行后显示下面的界面:

界面上有一个树图,树上显示某公司的人员组织结构,点击这些雇员,会在下面出现这个人的工资。现在程序中有两棵树:一棵是画面上实际的树,另一个是公司中雇员的虚拟的树。画面上的树节点是TreeNode类型,雇员的虚拟树节点是AbstractEmployee类型。我们可以采用组合模式,创造一种新的“节点”,组合这两种节点的特性,简化窗体类需要处理的情况,请看下面的代码:

public class EmpNode:TreeNode {

private AbstractEmployee emp;

 

public EmpNode(AbstractEmployee aemp ):base(aemp.getName ()) {

emp = aemp;

}

 

//-----

public AbstractEmployee getEmployee() {

return emp;

}

}

EmpNode类是TreeNode类的子类,他具有TreeNode类的所有特性,同时他也组合了AbstractEmployee类型的特点。这样以来调用者的工作就简化了。下面是Form类的代码片断,我把自动生成的代码省略了一部分:

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是为了将画面上的显示程序与后台的业务数据程序分离开。画面的显示程序只需要关心“雇员”提供哪些接口就可以工作了,而不去过问具体的细节,比如工资的计算规则。如果需要对界面类和数据类分别进行单元测试,这样的做法也提供了可能(也就是说,这个程序是可测试的)。测试画面的时候可以在雇员接口上实现一些虚假的雇员类,其中的方法和属性都是为了测试而假造的,这样就可以测试界面的显示是否正确。一般说来程序如果要进行单元测试,应该从设计阶段就考虑程序的“可测试性”,其中重要的一点是:将界面表示与业务逻辑分离开。

关于如何提高程序的可测试性,以后有时间我会整理一些心得体会。

时间: 2024-12-31 09:57:41

.NET中的设计模式三:组合模式的相关文章

C#设计模式(10)——组合模式(Composite Pattern)

原文:C#设计模式(10)--组合模式(Composite Pattern) 一.引言 在软件开发过程中,我们经常会遇到处理简单对象和复合对象的情况,例如对操作系统中目录的处理就是这样的一个例子,因为目录可以包括单独的文件,也可以包括文件夹,文件夹又是由文件组成的,由于简单对象和复合对象在功能上区别,导致在操作过程中必须区分简单对象和复合对象,这样就会导致客户调用带来不必要的麻烦,然而作为客户,它们希望能够始终一致地对待简单对象和复合对象.然而组合模式就是解决这样的问题.下面让我们看看组合模式是

【设计模式】—— 组合模式Composite

模式意图 使对象组合成树形的结构.使用户对单个对象和组合对象的使用具有一致性. 应用场景 1 表示对象的 部分-整体 层次结构 2 忽略组合对象与单个对象的不同,统一的使用组合结构中的所有对象. 模式结构 [安全的组合模式] 这种组合模式,叶子节点,也就是单个对象不具有对象的控制功能.仅仅有简单的业务操作. 1 package com.xingoo.composite.safe; 2 3 import java.util.ArrayList; 4 import java.util.List; 5

乐在其中设计模式(C#) - 组合模式(Composite Pattern)

原文:乐在其中设计模式(C#) - 组合模式(Composite Pattern)[索引页][源码下载] 乐在其中设计模式(C#) - 组合模式(Composite Pattern) 作者:webabcd 介绍 将对象组合成树形结构以表示"部分-整体"的层次结构.它使得客户对单个对象和复合对象的使用具有一致性. 示例 有一个Message实体类,使其单个对象和复合对象具有一致性. MessageModel using System;using System.Collections.Ge

iOS设计模式之组合模式

组合模式(Composite) 基本理解 整体和部分可以一直对待. 组合模式:将对象组合成树形结构以表示"部分--整体"的层次结构.组合模式使得用户对单个对象和组合独享的使用具有一致性. 透明方式和安全方式 透明方式:在Component(为组合中的对象声明接口)中声明所有用来管理子对象的方法 .这样实现该接口的子类都具有了该接口中的方法.这样的好处就是叶节点和枝节点对于外界没有区别,他们具有完全一致的行为接口.但问题也很明显,因为Leaf类本身不具备添加删除方法的功能,所以实现它是没

设计模式:组合模式(Composite)

将对象组合成属性结构以表示"部分-整体"的层次结构.组合使得用户和单个对象和组合对象的使用具有一致性. 组合模式设计的角色: 1. Component:是组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为.声明一个接口用于访问和管理Component. 2. Leaf:在组合中表示叶子节点对象,叶子节点没有子节点. 3. Composite:定义树枝节点行为,用来存储子部件,在Component接口中实现与子部件有关操作,如增加和删除等. 举个简单例子(树枝和叶子) 1

C++设计模式之组合模式_C 语言

问题描述 上图,是一个公司的组织结构图,总部下面有多个子公司,同时总部也有各个部门,子公司下面有多个部门.如果对这样的公司开发一个OA系统,作为程序员的你,如何设计这个OA系统呢?先不说如何设计实现,接着往下看,看完了下面的内容,再回过头来想怎么设计这样的OA系统. 什么是组合模式? 在GOF的<设计模式:可复用面向对象软件的基础>一书中对组合模式是这样说的:将对象组合成树形结构以表示"部分-整体"的层次结构.组合(Composite)模式使得用户对单个对象和组合对象的使用

温故而知新:设计模式之组合模式(Composite)

场景: 如果想模拟windows的开始菜单,分析一下会发现里面的菜单项:有些有子菜单,有些则没有:因此大体可以将菜单类分为二类,设计代码如下:   /// <summary> /// 菜单的显示接口 /// </summary> public interface IMenu { void Show(); } /// <summary> /// 菜单基类 /// </summary> public class MenuBase { public string

Head First设计模式之组合模式

一.定义 将对象组合成树形结构来表现"整体-部分"层次结构. 组合能让客户以一致的方法处理个别对象以及组合对象. 主要部分可以被一致对待问题. 在使用组合模式中需要注意一点也是组合模式最关键的地方:叶子对象和组合对象实现相同的接口.这就是组合模式能够将叶子节点和对象节点进行一致处理的原因. 二.结构   组合模式主要包含三个角色 1.Component(抽象构件) : 为参加组合的对象定义了公共接口和默认行为,声明一个接口用于访问和管理Component子部件.  2.Composit

深入理解JavaScript系列(40):设计模式之组合模式详解_基础知识

介绍 组合模式(Composite)将对象组合成树形结构以表示"部分-整体"的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性. 常见的场景有asp.net里的控件机制(即control里可以包含子control,可以递归操作.添加.删除子control),类似的还有DOM的机制,一个DOM节点可以包含子节点,不管是父节点还是子节点都有添加.删除.遍历子节点的通用功能.所以说组合模式的关键是要有一个抽象类,它既可以表示子元素,又可以表示父元素. 正文 举个例子,有家餐厅提