国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > .NET > 正文

.NET中的設(shè)計(jì)模式三:組合模式

2024-07-10 13:03:10
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

組合模式(composite)是一種“結(jié)構(gòu)型”模式(structural)。結(jié)構(gòu)型模式涉及的對(duì)象為兩個(gè)或兩個(gè)以上,表示對(duì)象之間的活動(dòng),與對(duì)象的結(jié)構(gòu)有關(guān)。
先舉一個(gè)組合模式的小小例子:



如圖:系統(tǒng)中有兩種box:game box和internet box,客戶(hù)需要了解者兩個(gè)類(lèi)的接口分別進(jìn)行調(diào)用。為了簡(jiǎn)化客戶(hù)的工作,創(chuàng)建了xbox類(lèi),程序代碼如下:

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的方法,這樣,用戶(hù)面對(duì)的情況就大大的簡(jiǎn)化了,調(diào)用的代碼如下:

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

}

}


可以看見(jiàn),用戶(hù)只需要了解xbox的接口就可以了。

組合模式的應(yīng)用例子

組合模式適用于下面這樣的情況:兩個(gè)或者多個(gè)類(lèi)有相似的形式,或者共同代表某個(gè)完整的概念,外界的用戶(hù)也希望他們合而為一,就可以把這幾個(gè)類(lèi)“組合”起來(lái),成為一個(gè)新的類(lèi),用戶(hù)只需要調(diào)用這個(gè)新的類(lèi)就可以了。

下面舉一個(gè)例子說(shuō)明composite模式的一個(gè)實(shí)際應(yīng)用。下面的class視圖:



employee類(lèi)是abstractemployee接口的一個(gè)實(shí)現(xiàn),boss類(lèi)是employee的一個(gè)子類(lèi),empnode是從樹(shù)視圖的treenode類(lèi)繼承而來(lái)的。我們先看看代碼:

abstractemployee,這是一個(gè)接口,提供下列方法:

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類(lèi)是abstractemployee接口的一個(gè)實(shí)現(xiàn)

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接口和他的一個(gè)實(shí)現(xiàn)來(lái)看,下面很可能要將這個(gè)類(lèi)型的數(shù)據(jù)組合成一個(gè)樹(shù)的結(jié)構(gòu)。

boss類(lèi)是employee類(lèi)的派生,他重載了employee類(lèi)的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方法是一個(gè)遞歸調(diào)用,如果child不是leaf,就繼續(xù)調(diào)用下去。上面幾個(gè)類(lèi)表達(dá)了一個(gè)樹(shù)的結(jié)構(gòu),表示出了公司中的領(lǐng)導(dǎo)和雇員的級(jí)別關(guān)系。

現(xiàn)在我們看一下這個(gè)程序需要達(dá)到的目標(biāo),程序運(yùn)行后顯示下面的界面:



界面上有一個(gè)樹(shù)圖,樹(shù)上顯示某公司的人員組織結(jié)構(gòu),點(diǎn)擊這些雇員,會(huì)在下面出現(xiàn)這個(gè)人的工資。現(xiàn)在程序中有兩棵樹(shù):一棵是畫(huà)面上實(shí)際的樹(shù),另一個(gè)是公司中雇員的虛擬的樹(shù)。畫(huà)面上的樹(shù)節(jié)點(diǎn)是treenode類(lèi)型,雇員的虛擬樹(shù)節(jié)點(diǎn)是abstractemployee類(lèi)型。我們可以采用組合模式,創(chuàng)造一種新的“節(jié)點(diǎn)”,組合這兩種節(jié)點(diǎn)的特性,簡(jiǎn)化窗體類(lèi)需要處理的情況,請(qǐng)看下面的代碼:

public class empnode:treenode {

private abstractemployee emp;

 

public empnode(abstractemployee aemp ):base(aemp.getname ()) {

emp = aemp;

}

 

//-----

public abstractemployee getemployee() {

return emp;

}

}


empnode類(lèi)是treenode類(lèi)的子類(lèi),他具有treenode類(lèi)的所有特性,同時(shí)他也組合了abstractemployee類(lèi)型的特點(diǎn)。這樣以來(lái)調(diào)用者的工作就簡(jiǎn)化了。下面是form類(lèi)的代碼片斷,我把自動(dòng)生成的代碼省略了一部分:

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方法是樹(shù)圖點(diǎn)擊節(jié)點(diǎn)事件的響應(yīng)方法,用戶(hù)點(diǎn)擊節(jié)點(diǎn)后在文本欄里顯示相應(yīng)的工資。組合模式已經(jīng)介紹完了,下面的東西和組合模式?jīng)]有什么關(guān)系。

為什么用interface

為什么要在程序中創(chuàng)建雇員的interface呢?我們可以創(chuàng)建一個(gè)class employee,再派生出boss,一樣可以實(shí)現(xiàn)上面的功能嘛。

使用interface是為了將畫(huà)面上的顯示程序與后臺(tái)的業(yè)務(wù)數(shù)據(jù)程序分離開(kāi)。畫(huà)面的顯示程序只需要關(guān)心“雇員”提供哪些接口就可以工作了,而不去過(guò)問(wèn)具體的細(xì)節(jié),比如工資的計(jì)算規(guī)則。如果需要對(duì)界面類(lèi)和數(shù)據(jù)類(lèi)分別進(jìn)行單元測(cè)試,這樣的做法也提供了可能(也就是說(shuō),這個(gè)程序是可測(cè)試的)。測(cè)試畫(huà)面的時(shí)候可以在雇員接口上實(shí)現(xiàn)一些虛假的雇員類(lèi),其中的方法和屬性都是為了測(cè)試而假造的,這樣就可以測(cè)試界面的顯示是否正確。一般說(shuō)來(lái)程序如果要進(jìn)行單元測(cè)試,應(yīng)該從設(shè)計(jì)階段就考慮程序的“可測(cè)試性”,其中重要的一點(diǎn)是:將界面表示與業(yè)務(wù)邏輯分離開(kāi)。

關(guān)于如何提高程序的可測(cè)試性,以后有時(shí)間我會(huì)整理一些心得體會(huì)。

發(fā)表評(píng)論 共有條評(píng)論
用戶(hù)名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 海兴县| 横山县| 新兴县| 德保县| 佳木斯市| 英超| 东兴市| 蛟河市| 晋宁县| 阜宁县| 从化市| 小金县| 公主岭市| 张家港市| 太保市| 赤壁市| 湖口县| 新建县| 东乌| 定南县| 彰武县| 赫章县| 兰西县| 仙游县| 湘阴县| 阆中市| 铁力市| 巴楚县| 佳木斯市| 淮南市| 康保县| 嘉峪关市| 平谷区| 澄城县| 包头市| 郴州市| 山东省| 庄浪县| 榆社县| 庄浪县| 会同县|