设计模式(2) 生成器模式(BUILDER)
问题聚焦:
生成器模式是对象创建型模式的一种
它将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
意图:
正如上面所说,生成器的意图是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
动机:以一个例子解释生成器的动机
Demo:
一个具有格式转换供功能的阅读器应该能将原始的RTF格式转换为多种正文格式。目的格式的种类可以任意类型的,因此要求是要很容易地实现新的转换的增加,同时不改变RTF阅读器。
解决办法:步骤
- 使用一个辅助类,来配置这个类(比如是RTFReader),这个辅助类(比如是TextConverter类)可以将原始的RTF格式转换成另一种格式的文本表示;
- RTFReader类(又称为导向器)专注于对文档的词法分析,而将每个单词或者符号的转换交给TextConverter;
- TextConverter对象(称为生成器)负责对数据进行转换以及用特定格式表示该标记;
- TextConvert的子类对不同转换和不同格式进行特殊处理。
目的:重用RTFReader的词法分析算法,根据目的格式的不同,使用不同的TextConverter的子类配置该RTFReader。
设计:
适用性:
在以下情况使用Buider模式
- 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时
- 当构造过程必须允许被构造的对象有不同的表示时
结构:
参与者:
- Builder(TextConverter):为创建一个Product对象的各个部件指定抽象接口
- ConcreteBuilder(ASCIIConverter、TeXConverter、TextWidgetConverter) : 实现Builder的接口以及构造和装配该产品的各个部件; 定义并明确它所创建的表示; 提供一个检索产品的接口
- Director(RTFReader):构造一个使用Builder接口的对象
- Product(ASCIIText、TeXText、TextWidget):表示被构建的复杂对象,包含定义组成部件的类,包括将这些部件装配成最终产品的接口
协作:
- 客户创建Director对象,并用他想要的Builder对象进行配置
- 一旦产品部件被生成,导向器就会通知生成器
- 生成器处理导向器的请求,并将部件添加到该产品中
- 客户从生成器中检索产品
效果:
- 它使你可以改变一个产品的内部表示:Buidler对象给使用它的导向器提供一个抽象接口,产品是通过这个抽象接口构造的,你在生产一个新的产品时需要定义一个新的生成器
- 它将构造代码和表示代码分开:Builder模式通过封装一个复杂对象的创建和表示方式提高了对象的模块性
- 它使你可对构造过程进行更精细的控制:Builder模式与一下子就生成产品的创建型模式不同,它是在导向者的控制下一步一步构造产品。因此可以更好地反映产品的构造过程。
实现:
生成器模式的实现需要考虑下面的几个问题
- 装配和构造接口:生成器逐步地构造它们的产品,因此Builder类接口必须足够普遍,以便为各种类型的具体生成器构造产品;
- 产品没有抽象类:各个产品相差很大,所以并没有必要定义一个抽象类继承它;
- 在Builder中缺省的方法为空:C++中,各个构建的生成方法故意不声明为纯虚成员函数,而是把它们是定义为空方法,这使客户只重定义他们所感兴趣的操作。
代码示例:
依然以创建迷宫为例:
这里我们定义一个CreateMaze成员函数的变体,它以类MazeBuilder的一个生成器对象作为参数。
MazeBuilder类定义如下
class MazeBuilder { public: virtual void BuildMaze() {} virtual void BuildRoom(int room) {} virtual void BuildDoor(int roomFrom, int roomTo) {} virtual Maze* GetMaze() { return 0;} protected: MazeBuilder(); }
以生成器为参数的CreateMaze成员函数版本
Maze* MazeGame::CreateMaze (MazeBuilder& builder) { builder.BuildMaze(); builder.BuildRoom(1); builder.BuildRoom(2); builder.BuildDoor(1,2); return builder.GetMaze(); }
关于生成器的子类对具体的迷宫的实现的代码这里不再贴出,下面演示一下怎样使用一个生成器子类CountingMazeBuilder
int rooms, doors; MazeGame game; CountingMazeBuilder builder; game.CreateMaze(builder); builder.getCounts(rooms, doors);
相关模式
抽象工厂和生成器模式的比较:
共同点:都可以创建复杂对象区别:Builder模式着重一步步构造一个复杂对象,这个产品可能在导向器和生成器之间传递很多次,最后返回产品抽象工厂着重于产品的多个系列,而且产品是在工厂中一次加工好然后返回的,并没有出现产品的半成品返厂的情况出现(这点可以将两个模式的示例代码比较一下,就会有感触)
参考资料:
《设计模式》