一 意圖
將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創(chuàng)建不同的表示。
二 適用性
在以下情況使用Build模式:
1 當創(chuàng)建復雜對象的算法應該獨立于該對象的組成部分以及它們的裝配方式時。
2 當構造過程必須允許被構造的對象有不同的表示時。
3 Builder模式要解決的也正是這樣的問題:
當我們要創(chuàng)建的對象很復雜的時候(通常是由很多其他的對象組合而成),
我們要復雜對象的創(chuàng)建過程和這個對象的表示(展示)分離開來,
這樣做的好處就是通過一步步的進行復雜對象的構建,
由于在每一步的構造過程中可以引入?yún)?shù),使得經過相同的步驟創(chuàng)建最后得到的對象的展示不一樣。
在書中第一個例子RTF文檔閱讀器的實現(xiàn)中,可以看到文檔RTFReader支持。
從此圖中可以看到:
1封裝了三種復雜對象的構建:
ASCIIText,TeXText,TextWiWdget,分別對應不同的builder
2 同樣的創(chuàng)建過程創(chuàng)建不同的表示
??? 可以在RTFReader中對文檔進行解析的時候while循環(huán),對于同樣的文檔,使用不同builder創(chuàng)建產品,同樣過程可以得到不同的表示。
3 復雜對象構建分過程進行
??? 在while循環(huán)中,可以看到對不同類型的文檔構件,處理的方式不同。分成不同的部分進行處理。
?
三 結構圖
?
四 交互過程
Director:是構造一個使用Builder接口的對象
Client創(chuàng)建Director對象,并用它所想要的Builder對象進行配置。
Director創(chuàng)建和裝配對象過程
?
五 代碼實現(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; }
《形似神不似》
六 實例分析
?
?
在這個例子中:VcpTextView支持以下幾種顯示方式:
UnicodeText,RichText,IconObject,CoustomObject。
每一種之間都是獨立不可替換的,相對具有比較復雜的算法。
在顯示的時候使用VcpTextBasicLayout來導向裝配顯示各元素。
但是在大多數(shù)實際應用中很多情況,不可能完全的找出書中所描述的情形,
大多數(shù)只是在某一部分是符合Builder模式。
?
七 分析總結
效果:
1 隱藏產品的內部表示
Builder提供創(chuàng)建產品的接口給Director,
隱藏了產品的內部結構(僅提供接口BuildPart()創(chuàng)建產品)
隱藏該產品是如何裝配的(BuildPart()內部裝配是隱藏的)。
2 將構造代碼和表示代碼分開
構造代碼是在Builder提供的接口中完成的,每個ConcreateBuilder包含了創(chuàng)建和裝配一個特定產品的所有代碼。
提供不同的Builder,使用相同的Director導向過程可以得到不同的表示。
使用的不同Client可以使用相同的Builder,得到不同相同的表示。
在前面RTFReader閱讀器的例子中:
如果提供ASCIIText Converter 則只能得到ASCIIText,提供TexText Converter則可以得到TexText。
如果使用XMLReader,提供ASCIIText Converter 使用Director得到不同于的ASCIIText的表示。
所以將構造代碼和表示代碼分開,可以使代碼得到重用。
3 精確的控制導向產品的創(chuàng)建
將代碼的構建過程委托為Director去完成,那么Client可以不用關注產品的構建過程
何時完成或者完成到什么程度,交給Director去控制產品的創(chuàng)建和裝配的過程。并返回所創(chuàng)建的產品,或者通知Client。
?
在實際的使用情況中可能都并非如此,大多數(shù)只能在某些部分匹配Builder模式。