小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

亂砍設(shè)計模式之四

 wtf_soft 2006-04-18
COMPOSITE與BUILDER模式 —— 忠義堂石碣受天文 梁山泊英雄排座次

junguo

     Composite模式的中文名字是組合模式,該模式的目的是使單個對象和它的對象組合(一般是數(shù)組或者鏈表的結(jié)構(gòu))擁有統(tǒng)一的操作方式,這樣可以簡化客戶的使用。我們還是通過具體的例子來理解該模式。還是先來一段例子背景介紹:
     話說,宋江帶人攻陷東平東昌兩郡,收降了雙槍將董平和沒羽箭張清,而后皇甫端來歸。梁山聚集了武將,書生,醫(yī)生以至獸醫(yī)等各色人等,成為了一個成熟的社區(qū)。著點人數(shù)正好一百單八人,是個吉利數(shù)字,為此宋江組織了浩大的祭天儀式。在祭天儀式進行期間,發(fā)現(xiàn)了埋于地下的石碣,石碣上排定了三十六天罡,七十二地煞的座次。次序以定,梁山也開始走上正規(guī),開始劃分各位好漢的職責(zé)。依據(jù)各人特長,分別任職于馬軍,步軍,軍師等角色。有了規(guī)矩,才成方圓,自此梁山告別了單挑的時代,作戰(zhàn)有了一些固定的組合。如李逵出戰(zhàn),必有項充,李袞護在身邊;這位頭腦簡單的殺人魔王的典型特點是攻強守弱,而且不著盔甲,如若無人保護估摸早死了。還有另外一些組合:如矮腳虎王英和一丈青扈三娘這樣的夫妻組合(扈三娘嫁給了王英,你能想通嗎?我想不通);還有解寶解珍這樣的兄弟組合等。
     來考慮一下,如何實現(xiàn)這樣的功能,武將可以單獨出擊,也可以成群的出擊?該如何去實現(xiàn)這樣的功能呢?還是一步一步來,先看類圖:

    這是我們建立的武將類,該類和我們第一講《趙子龍單騎救主》中建立的武將類相同(為了突出中心功能,簡化了功能)??紤]一下在這種情況下,我們?nèi)绻胱尪鄠€武將一起出擊,該如何實現(xiàn)?最單純的做法就是在調(diào)用該類的地方生成一個數(shù)組,然后客戶端通過該數(shù)組來調(diào)用各個對象的操作。大概代碼如下:
   General general[10];
for( int  i = 0; i<10; i++  )
general[i].Assault();

    客戶端也可以將這些操作封裝在不同的函數(shù)中,但如果武將類填加新的函數(shù)的時候,客戶端也需要相應(yīng)的增加此類德函數(shù)。這是典型的封裝不好的例子,客戶端需要增加特殊的函數(shù)來支持對象。我自己的工作中就遇到了這樣的問題,類沒有封裝好,針對于這些類的操作,你不得不填加一些函數(shù),接著就可以發(fā)現(xiàn)在不同的文件里有很多功能相似的函數(shù)(一個類往往在多處用到,而這些往往是由不同的人寫的,這樣很容易造成重復(fù)代碼)。整個程序被改得亂碼七糟的,改這樣的東西真是頭痛萬分,更加上你如果要用到這些外部的函數(shù)的時候,都不知道用那個。那如何來解決該問題呢?找到組合模式,就找到了解決之道。先來看看組合模式的類圖描述:

    Component是為Leaf和Composite抽象出來的共同接口,可以通過它來統(tǒng)一操作單個對象或者組合對象。
    Leaf是我們具體要用到的類,它用來完成具體的功能。Composite是我們用來組合對象的類,并實現(xiàn)對子對象的管理;它通過Add,Remove和GetChild來實現(xiàn)對子對象的管理。Composite中會含有一個Component型的對象列表childers,它也會重載Leaf中所需要的函數(shù),然后通過遍歷執(zhí)行所有childer的函數(shù)。該childer也可以是Composite型的,這樣就可以有一個遞歸的結(jié)構(gòu)。
    我們用Composite模式來重新設(shè)計我們的類圖,如下:

     理解模式最有效的方式,還是看具體的代碼:
#include 
#include 
#include 
#include 
using namespace std;
//抽象部件類,是Leaf和Composite的共有接口
class GeneralComponent
{
public:
//為了確保調(diào)用到析構(gòu)函數(shù),此處聲明為虛函數(shù)
virtual ~GeneralComponent(){}
//由于General中不實現(xiàn)Add,Remove,GetChild,此處需要一個缺省的實現(xiàn),但此處拋出異常更合適一些,為了簡單,省略了。
virtual void Add(GeneralComponent* pComonent) {}
virtual void Remove(GeneralComponent* pComonent) {}
virtual GeneralComponent* GetChild(int i) {return NULL;}
virtual void Assault() = 0;
};
//具體用到的類
class General : public GeneralComponent
{
private:
string m_strName;
public:
General(string strName):m_strName(strName){}
void Assault()
{
cout << m_strName << " 進入戰(zhàn)斗!" << endl;
}
};
//組合類
class GeneralComposite : public GeneralComponent
{
private:
string m_strName;
//用來存放需要組合的對象
vector m_pComponents;
public:
GeneralComposite(string strName):m_strName(strName){}
virtual ~GeneralComposite()
{
vector::iterator pos;
for( pos = m_pComponents.begin();pos::iterator ivite=
find(m_pComponents.begin(),m_pComponents.end(),pGeneral);
m_pComponents.erase(ivite);
}
GeneralComponent* GetChild(int i)
{
return m_pComponents[i];
}
void Assault()
{
cout << m_strName << "戰(zhàn)斗序列" << endl;
//需要調(diào)用所有的組合對象的操作
vector::iterator pos;
for( pos = m_pComponents.begin();posAssault();
}
}
};

    我們再來看一下,客戶端如何來調(diào)用該對象:
int main(int argc, char* argv[])
{
GeneralComposite pArmy("梁山大軍");
GeneralComposite* pHorseArmy = new GeneralComposite("梁山馬軍");
GeneralComposite* pPaceArmy = new GeneralComposite("梁山步軍");
General *pWusong = new General("好漢武松");
General *pHuaheshang = new General("俠客魯智深");
General *pLida = new General("莽漢李逵");
General *pLinchong = new General("英雄林沖");
General *pGuansheng = new General("大刀關(guān)勝");
pHorseArmy->Add(pLinchong);
pHorseArmy->Add(pGuansheng);
pPaceArmy->Add(pWusong);
pPaceArmy->Add(pHuaheshang);
pPaceArmy->Add(pLida);
pArmy.Add(pHorseArmy);
pArmy.Add(pPaceArmy);
pArmy.Assault();
return 0;
}

     運行結(jié)果如下:

     我們可以看到,通過組合模式,對于對象的調(diào)用變得簡單了起來,只要通過一個函數(shù)就可以實現(xiàn)對所有對象的統(tǒng)一調(diào)用。這樣做最大的好處就是將所有的代碼統(tǒng)一成了一份,可以避免在不同的地方出現(xiàn)類似的代碼。在多人合作的情況下,由于交流不充分的問題,很多時候開發(fā)人員各自寫各自的代碼,而不關(guān)心別人的代碼,極容易產(chǎn)生相同功能的代碼。當(dāng)我們的類進行擴展的時候,可能很多地方都需要進行修改。
    我們再來看看組合模式可能存在的問題。Leaf類繼承了Component中的所有方法,但對于Add,Remove,GetChild等操作,這些操作對它來說是無意義的。我們也可以把這些操作放到Composite中,但在這種情況下,我們將無法通過基類來直接操作Composite對象。個人覺得這個問題可以根據(jù)具體問題權(quán)衡解決。我們也可以為Component擴展更多的Leaf或者Composite,在這種情況下,如果我們想控制Composite中只能添加限定的Leaf或者Composite在靜態(tài)情況下是不可能的,必須在運行時刻去判斷。
    還有,我們應(yīng)該最大化Component接口,我們使用該接口的目的就是使用戶不知道它具體操作的是哪個Leaf或者Composite,所以我們應(yīng)該盡量多定義一些它們公共的操作。這樣還是會造成我們上面提到的會給一些類帶來無意義的功能。
    我們再來簡單比較一下Composite和Decorator模式,這兩個模式都會有一個子類擁有基類的指針,不同之處是Composite會擁有多個組件,而Decorator只擁有一個組件。Composite主要目的是對象的聚集,使它們的調(diào)用統(tǒng)一化;而Decorator不是此目的,它的目的是給對象添加一些額外的功能。
    好了,Composite的講解先到此為止。如果你感興趣的話,可以試著把我們第一章用到的策略模式應(yīng)用到該處。不過我們下面看另外一個問題。在main中,我們需要很多代碼去完成對象的創(chuàng)建。我們是否可以將對象的創(chuàng)建部分也做一些封裝呢?我們可以找找創(chuàng)建型的模式中是否有適合我們的模式?
    在《設(shè)計模式》中,我們可以找到Builder模式,它的定義是:將一個復(fù)雜對象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。和我們的意圖有點象,先來看看《設(shè)計模式》中為我們提供的類圖:

     作一下解釋:
    1, Director是具體要用到復(fù)雜對象的部分(如GeneralComponent)的類(我們程序中是main中調(diào)用)。它會通過Builder來建造自己需要的對象,而不需要自己通過new來得到所要的對象。
    2, 而Builder的是一個抽象的創(chuàng)建對象的接口。
    3, ConcreteBuilder是我們用來具體創(chuàng)建對象的類。它可能有多個,用來幫我們建造不同類型的對象。如我們把我們的General派生出不同的風(fēng)格,如先秦風(fēng)格和羅馬風(fēng)格的時候,我們可能需要生成不同的類。而每次創(chuàng)建一種風(fēng)格的對象,我們就可以擴展Builder而生成不同的創(chuàng)建類。
    我們的例子比較簡單,不需要這么多東西。Builder的重要特征是:它是用來一步一步創(chuàng)建復(fù)雜對象類型的。我們的組合類由于需要組合不同的組件,所以整個創(chuàng)建過程比較復(fù)雜,那么可以借用Builder模式的特點來創(chuàng)建我們的對象。好的,看看具體的代碼:
class Builder
{
public:
static GeneralComponent* CreateCompise(GeneralComponent *pParent,string strArr[],int iLength = 1)
{
if ( pParent != NULL )
{
for( int i = 0 ; i < iLength ; i++ )
{
pParent->Add(new GeneralComposite(strArr[i]));
}
return pParent;
}
else
{
return new GeneralComposite(strArr[0]);
}
}
static GeneralComponent* CreateGeneral(GeneralComponent *pParent,string strArr[],int iLength = 1)
{
if ( pParent != NULL )
{
for( int i = 0 ; i < iLength ; i++ )
{
pParent->Add(new General(strArr[i]));
}
return pParent;
}
else
{
return new General(strArr[0]);
}
}
};

    這邊的代碼略顯簡單,只重意了,你可以看到,創(chuàng)建過程被封裝成CreateCompise和CreateGeneral兩個方法,通過它們來創(chuàng)建我們所需要的對象。只要將所需要的Composite(為它創(chuàng)建Leaf和子Composite的),還有創(chuàng)建對象所需要的參數(shù)(例子有些簡單,只要一個初始化參數(shù))數(shù)組和數(shù)組長度傳進函數(shù)就可以幫我們創(chuàng)建所需要的對象了。由于Builder沒有成員變量,將兩個函數(shù)設(shè)置成了靜態(tài)函數(shù),想當(dāng)于一個單件類型。我們再看看它的具體用法:
int main(int argc, char* argv[])
{
string strArmy[1] = {"梁山大軍"};
string strArmyChild[2] = {"梁山馬軍","梁山步軍"};
string strSpecLeader[3] = {"好漢武松","俠客魯智深","莽漢李逵"};
string strHorseLeader[2] = {"英雄林沖","大刀關(guān)勝"};
GeneralComponent *pArmy = Builder::CreateCompise(NULL,strArmy);
Builder::CreateCompise(pArmy,strArmyChild,2);
Builder::CreateGeneral(pArmy->GetChild(0),strHorseLeader,2);
Builder::CreateGeneral(pArmy->GetChild(1),strSpecLeader,3);
pArmy->Assault();
delete pArmy;
}

     這樣我們就可以統(tǒng)一對象的創(chuàng)建過程。做到一定程度的聚合。我個人感覺模式的學(xué)習(xí)過程是從形到意(就是從一些具體的例子看起,通過具體的例子來理解模式);而后是重意不重形(明白模式的意義之后,我們就可以隨意使用模式了,而不需要硬套公式)。所以此處使用了和書上并不一致的Builder模型。其實Builder模式與Abstract Factory模式很相似,我們下次描述Abstract Factory模式的時候,再回頭比較一下。
參考書目:
1,	設(shè)計模式——可復(fù)用面向?qū)ο筌浖幕A(chǔ)(Design Patterns ——Elements of Reusable Object-Oriented Software) Erich Gamma 等著 李英軍等譯  機械工業(yè)出版社
2,	Head First Design Patterns(影印版)Freeman等著 東南大學(xué)出版社
3,	道法自然——面向?qū)ο髮嵺`指南    王詠武 王詠剛著  電子工業(yè)出版社
4, 水滸傳 —— 網(wǎng)上找到的電子檔

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多