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

///

1<summary>
2
3///  Required designer variable. 
4
5///  </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;

</


Published At
Categories with Web编程
Tagged with
comments powered by Disqus