一 意图
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
二 适用性
在以下情况使用Build模式:
1 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
2 当构造过程必须允许被构造的对象有不同的表示时。
3 Builder模式要解决的也正是这样的问题:
当我们要创建的对象很复杂的时候(通常是由很多其他的对象组合而成),
我们要复杂对象的创建过程和这个对象的表示(展示)分离开来,
这样做的好处就是通过一步步的进行复杂对象的构建,
由于在每一步的构造过程中可以引入参数,使得经过相同的步骤创建最后得到的对象的展示不一样。
在书中第一个例子RTF文档阅读器的实现中,可以看到文档RTFReader支持。
从此图中可以看到:
1封装了三种复杂对象的构建:
ASCIIText,TeXText,TextWiWdget,分别对应不同的builder
2 同样的创建过程创建不同的表示
可以在RTFReader中对文档进行解析的时候while循环,对于同样的文档,使用不同builder创建产品,同样过程可以得到不同的表示。
3 复杂对象构建分过程进行
在while循环中,可以看到对不同类型的文档构件,处理的方式不同。分成不同的部分进行处理。
三 结构图
四 交互过程
Director:是构造一个使用Builder接口的对象
Client创建Director对象,并用它所想要的Builder对象进行配置。
Director创建和装配对象过程
五 代码实现
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来导向装配显示各元素。
但是在大多数实际应用中很多情况,不可能完全的找出书中所描述的情形,
大多数只是在某一部分是符合Builder模式。
七 分析总结
效果:
1 隐藏产品的内部表示
Builder提供创建产品的接口给Director,
隐藏了产品的内部结构(仅提供接口BuildPart()创建产品)
隐藏该产品是如何装配的(BuildPart()内部装配是隐藏的)。
2 将构造代码和表示代码分开
构造代码是在Builder提供的接口中完成的,每个ConcreateBuilder包含了创建和装配一个特定产品的所有代码。
提供不同的Builder,使用相同的Director导向过程可以得到不同的表示。
使用的不同Client可以使用相同的Builder,得到不同相同的表示。
在前面RTFReader阅读器的例子中:
如果提供ASCIIText Converter 则只能得到ASCIIText,提供TexText Converter则可以得到TexText。
如果使用XMLReader,提供ASCIIText Converter 使用Director得到不同于的ASCIIText的表示。
所以将构造代码和表示代码分开,可以使代码得到重用。
3 精确的控制导向产品的创建
将代码的构建过程委托为Director去完成,那么Client可以不用关注产品的构建过程
何时完成或者完成到什么程度,交给Director去控制产品的创建和装配的过程。并返回所创建的产品,或者通知Client。
在实际的使用情况中可能都并非如此,大多数只能在某些部分匹配Builder模式。