Builder模式:復(fù)雜對象的構(gòu)建
一 意圖
將一個(gè)復(fù)雜對象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。
二 適用性
在以下情況使用Build模式:
1 當(dāng)創(chuàng)建復(fù)雜對象的算法應(yīng)該獨(dú)立于該對象的組成部分以及它們的裝配方式時(shí)。
2 當(dāng)構(gòu)造過程必須允許被構(gòu)造的對象有不同的表示時(shí)。
3 Builder模式要解決的也正是這樣的問題:
當(dāng)我們要?jiǎng)?chuàng)建的對象很復(fù)雜的時(shí)候(通常是由很多其他的對象組合而成),
我們要復(fù)雜對象的創(chuàng)建過程和這個(gè)對象的表示(展示)分離開來,
這樣做的好處就是通過一步步的進(jìn)行復(fù)雜對象的構(gòu)建,
由于在每一步的構(gòu)造過程中可以引入?yún)?shù),使得經(jīng)過相同的步驟創(chuàng)建最后得到的對象的展示不一樣。
在書中第一個(gè)例子RTF文檔閱讀器的實(shí)現(xiàn)中,可以看到文檔RTFReader支持。
從此圖中可以看到:
1封裝了三種復(fù)雜對象的構(gòu)建:
ASCIIText,TeXText,TextWiWdget,分別對應(yīng)不同的builder
2 同樣的創(chuàng)建過程創(chuàng)建不同的表示
??? 可以在RTFReader中對文檔進(jìn)行解析的時(shí)候while循環(huán),對于同樣的文檔,使用不同builder創(chuàng)建產(chǎn)品,同樣過程可以得到不同的表示。
3 復(fù)雜對象構(gòu)建分過程進(jìn)行
??? 在while循環(huán)中,可以看到對不同類型的文檔構(gòu)件,處理的方式不同。分成不同的部分進(jìn)行處理。
?
三 結(jié)構(gòu)圖
?
四 交互過程
Director:是構(gòu)造一個(gè)使用Builder接口的對象
Client創(chuàng)建Director對象,并用它所想要的Builder對象進(jìn)行配置。
Director創(chuàng)建和裝配對象過程
?
五 代碼實(shí)現(xiàn)
?1 Product
?
/************************************************************************ ?*?Product?Controls?????????????????????????????????????????????????????* ?***********************************************************************/ /*********************************************** ?*?Class?Frame?????????????????????????????????* ?**********************************************/ class?Frame { public: virtual?void?draw()?=?0; }; /*********************************************** ?*?Class?Title?????????????????????????????????* ?**********************************************/ class?Title?:?public?Frame { public: virtual?void?draw() ????{ ????????cout<<"title?draw"<<endl; } }; class?TextTitle:?public?Title { public: virtual?void?draw() ????{ ????????cout<<"TextTitle?draw"<<endl; } }; class?ImageTitle:?public?Title { public: virtual?void?draw() ????{ ????????cout<<"ImageTitle?draw"<<endl; } }; /*********************************************** ?*?Class?Menu??????????????????????????????????* ?**********************************************/ class?Menu?:?public?Frame { public: virtual?void?draw() ????{ ????????cout<<"menu?draw"<<endl; } }; class?ListMenu:?public?Menu { public: virtual?void?draw() ????{ ????????cout<<"ListMenu?draw"<<endl; } }; class?ThreeDMenu:?public?Menu { public: virtual?void?draw() ????{ ????????cout<<"3DMenu?draw"<<endl; } }; /*********************************************** ?*?Class?Toolbar???????????????????????????????* ?**********************************************/ class?Toolbar?:?public?Frame { public: virtual?void?draw() ????{ ????????cout<<"Toolbar?draw"<<endl; } }; class?CellToolbar?:?public?Toolbar { public: virtual?void?draw() ????{ ????????cout<<"CellToolbar?draw"<<endl; } }; class?FloatToolbar?:?public?Toolbar { public: virtual?void?draw() ????{ ????????cout<<"FloatToolbar?draw"<<endl; } }; /*********************************************** ?*?Class?Button????????????????????????????????* ?**********************************************/ class?Button?:?public?Frame { public: virtual?void?draw() ????{ ????????cout<<"Button?draw"<<endl; } }; class?TextButton?:?public?Button { public: virtual?void?draw() ????{ ????????cout<<"TextButton?draw"<<endl; } }; class?ImageButton?:?public?Button { public: virtual?void?draw() ????{ ????????cout<<"CellToolbar?draw"<<endl; } }; class?ThreeDButton?:?public?Button { public: virtual?void?draw() ????{ ????????cout<<"ThreeDButton?draw"<<endl; } }; /*********************************************** ?*?Class?Page??????????????????????????????????* ?**********************************************/ class?Page?:?public?Frame { public: #define?FRAME_MAX???10 ????Page() ????{ ????????m_frame_num?=?0; ????} void?AddFrame(Frame*?frm) ????{ if?(m_frame_num?<?FRAME_MAX) ????????{ ????????????m_frame[m_frame_num]?=?frm; ????????????m_frame_num++; ????????} ????} virtual?void?draw() ????{ ????????cout<<"page?draw"<<endl; for?(int?i?=0;?i?<?m_frame_num;?i++) ????????{ ????????????m_frame[i]->draw(); ????????} ????} private: ????Frame*?m_frame[FRAME_MAX]; int?m_frame_num; }; class?SlidePage?:?public?Page { public: virtual?void?draw() ????{ ????????Page::draw(); ????????cout<<"SlidePage?draw"<<endl; } }; class?VaryPage?:?public?Page { public: virtual?void?draw() ????{ ????????Page::draw(); ????????cout<<"VaryPage?draw"<<endl; } };
?
2 Builder
/************************************************************************ ?*?Build?ControlBuilder?????????????????????????????????????????????????* ?***********************************************************************/ /*********************************************** ?*?Class?ControlBuilder????????????????????????* ?**********************************************/ class?ControlBuilder { protected: ????ControlBuilder(){} public: virtual?void?BuildTitle()???{???} virtual?void?BuildMenu()????{???} virtual?void?BuildToolbar()?{???} virtual?void?BuildButton()??{???} virtual?void?BuildPage()????{???} virtual?Page*?GetPage()?????{return?NULL;} }; /*********************************************** ?*?Class?GenerralControlBuilder????????????????* ?**********************************************/ class?GenerralControlBuilder:?public?ControlBuilder { public: virtual?void?BuildTitle() ????{ ????????Title*?tl?=?new?TextTitle(); ????????m_page->AddFrame(tl); ????} virtual?void?BuildMenu() ????{ ????????Menu*?mu?=??new?ListMenu(); ????????m_page->AddFrame(mu); ????} virtual?void?BuildToolbar() ????{ ????????Toolbar*?tb?=??new?CellToolbar(); ????????m_page->AddFrame(tb); ????} virtual?void?BuildPage() ????{ ????????m_page?=??new?SlidePage(); ????} virtual?Page*?GetPage() ????{ return?m_page; ????} private: ????Page*?m_page; }; /*********************************************** ?*?Class?MagicControlBuilder???????????????????* ?**********************************************/ class?MagicControlBuilder:?public?ControlBuilder { public: ????MagicControlBuilder() ????{ ????????m_page?=?NULL; ????} virtual?void?BuildTitle() ????{ ????????Title*?tl?=??new?ImageTitle(); ????????m_page->AddFrame(tl); ????} virtual?void?BuildMenu() ????{ ????????Menu*?mu?=?new?ThreeDMenu(); ????????m_page->AddFrame(mu); ????} virtual?void?BuildToolbar() ????{ ????????Toolbar*?tb?=?new?FloatToolbar(); ????????m_page->AddFrame(tb); ????} virtual?void?BuildButton() ????{ ????????Button*?btn?=?new?ThreeDButton(); ????????m_page->AddFrame(btn); ????} virtual?void?BuildPage() ????{ ????????m_page?=??new?VaryPage(); ????} virtual?Page*?GetPage() ????{ return?m_page; ????} private: ????Page*?m_page; };
3 Director
/************************************************************************ ?*?Director?PageDirector????????????????????????????????????????????????* ?***********************************************************************/ /*********************************************** ?*?Class?PageDirector??????????????????????????* ?**********************************************/ class?PageDirector { public: ????PageDirector(ControlBuilder*?builder) ????{ ????????m_builder?=?builder; ????} virtual?Page*?CreatePage() ????{ ????????m_builder->BuildPage(); ????????m_builder->BuildTitle(); ????????m_builder->BuildMenu(); ????????m_builder->BuildToolbar(); ????????m_builder->BuildButton(); return?m_builder->GetPage(); ????} private: ????ControlBuilder*?m_builder; };
4 Client
/************************************************************************ ?*?Client???????????????????????????????????????????????????????????????* ?***********************************************************************/ bool?ShowPage(Page*?pg) { ????pg->draw(); return?true; } int?main() { ????MagicControlBuilder*?mgcCtrl?=?new?MagicControlBuilder(); ????PageDirector*?drctr?=?new?PageDirector(mgcCtrl); ????drctr->CreatePage(); ????Page*?pg?=?mgcCtrl->GetPage(); ????ShowPage(pg); return?0; }
《形似神不似》
六 實(shí)例分析
?
?
在這個(gè)例子中:VcpTextView支持以下幾種顯示方式:
UnicodeText,RichText,IconObject,CoustomObject。
每一種之間都是獨(dú)立不可替換的,相對具有比較復(fù)雜的算法。
在顯示的時(shí)候使用VcpTextBasicLayout來導(dǎo)向裝配顯示各元素。
但是在大多數(shù)實(shí)際應(yīng)用中很多情況,不可能完全的找出書中所描述的情形,
大多數(shù)只是在某一部分是符合Builder模式。
?
七 分析總結(jié)
效果:
1 隱藏產(chǎn)品的內(nèi)部表示
Builder提供創(chuàng)建產(chǎn)品的接口給Director,
隱藏了產(chǎn)品的內(nèi)部結(jié)構(gòu)(僅提供接口BuildPart()創(chuàng)建產(chǎn)品)
隱藏該產(chǎn)品是如何裝配的(BuildPart()內(nèi)部裝配是隱藏的)。
2 將構(gòu)造代碼和表示代碼分開
構(gòu)造代碼是在Builder提供的接口中完成的,每個(gè)ConcreateBuilder包含了創(chuàng)建和裝配一個(gè)特定產(chǎn)品的所有代碼。
提供不同的Builder,使用相同的Director導(dǎo)向過程可以得到不同的表示。
使用的不同Client可以使用相同的Builder,得到不同相同的表示。
在前面RTFReader閱讀器的例子中:
如果提供ASCIIText Converter 則只能得到ASCIIText,提供TexText Converter則可以得到TexText。
如果使用XMLReader,提供ASCIIText Converter 使用Director得到不同于的ASCIIText的表示。
所以將構(gòu)造代碼和表示代碼分開,可以使代碼得到重用。
3 精確的控制導(dǎo)向產(chǎn)品的創(chuàng)建
將代碼的構(gòu)建過程委托為Director去完成,那么Client可以不用關(guān)注產(chǎn)品的構(gòu)建過程
何時(shí)完成或者完成到什么程度,交給Director去控制產(chǎn)品的創(chuàng)建和裝配的過程。并返回所創(chuàng)建的產(chǎn)品,或者通知Client。
?
在實(shí)際的使用情況中可能都并非如此,大多數(shù)只能在某些部分匹配Builder模式。