意图

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以表示不同的表示。

适用性

  • 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
  • 当构造过程必须允许被构造的对象有不同的表示时。

UML图

  • Builder ——为创建一个Product对象的各个部件指定抽象接口。
  • ConcreteBuilder ——实现Builder的接口以构造和装配该产品的各个部件。

    ——定义并明确它所创建的表示。

    ——提供一个检索产品的接口。

  • Director ——构造一个使用Builder接口的对象。
  • Product ——表示被构造的复杂对象。
  • ConcreteBuilder创建该产品的内部表示并定义它的装配过程。

    ——包含定义组成部件的类,包括将这些部件装配成最终产品的接口。

代码示例

  ConcreteBuilder中需要提供构建产品的的各个部件的函数,以及对外获取被创建产品的接口。还是以创建迷宫为例,迷宫产品组件包括迷宫自身、房间、门,这些组件创建函数(BuildPart)需定义为虚函数,以便子类继承实现。在产品各部分创建好了后,需要通过对外的接口GetResult来获取创建的产品,本例GetMaze返回创建的迷宫。

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();
};

  因为builder中BuildPart和GetResult函数都是虚函数,所以Director并不关心继承Builder的子类实现,Director只需要按部就班的调用Builder的BuildPart函数来创建产品,如下例:

builder.BuildMaze();//创建迷宫

builder.BuildRoom(1);//创建1号房间

builder.BuildRoom(2);//创建2号房间

builder.BuildDoor(1,2);//创建1号房间和2号房间的门

在产品各个部件创建完成后,调用:

build.GetMaze();//获取已创建迷宫

Maze* MazeGame::CreateMaze(MazeBuilder& builder){
builder.BuildMaze(); builder.BuildRoom(1);
builder.BuildRoom(2);
builder.BuildDoor(1,2); return builder.GetMaze();
}
MazeBuilder基类提供实现产品的接口,具体实现还需要子类来实现。这里StandardMazeBuilder继承MazeBuilder并实现了基本功能。
StandardMazeBuilder类声明:
class StandardMazeBuilder:public MazeBuilder{
public:
StandardMazeBuilder(); virtual void BuildMaze();
virtual void BuildRoom(int);
virtual void BuildDoor(int,int); virtual Maze* GetMaze();
private:
Direction CommonWall(Room *, Room *);
Maze * _currentMaze;
}

StandardMazeBuilder类实现:

#include "Builder.h"

StandardMazeBuilder::StandardMazeBuilder(){
_currentMaze = 0;
} void StandardMazeBuilder::BuildMaze(){
_currentMaze = new Maze;
} Maze* StandardMazeBuilder::GetMaze(){
return _currentMaze;
} void StandardMazeBuilder::BuildRoom(int n){
if(!_currentMaze->RoomNo(n)){
Room* room = new Room(n);
_currentMaze->AddRoom(room); room->SetSide(North, new Wall);
room->SetSide(South, new Wall);
room->SetSide(East, new Wall);
room->SetSide(West, new Wall);
}
} void StandardMazeBuilder::BuildDoor(int n1, int n2){
Room *r1 = _currentMaze->RoomNo(n1);
Room *r2 = _currentMaze->RoomNo(n2);
Door *d = new Door(r1,r2); r1->SetSide(CommonWall(r1,r2),d);
r2->SetSide(CommonWall(r2,r1),d);
}

   在实现了Director和Builder后,Client无需关心具体产品的具体实现,只需要新建该Builder类,传递给Director,就能创建出不同的产品了。

#include "Direcotr.h"
#include "StandardMazeBuilder.h" int Main(){ Maze *maze;
MazeGame game;
StandardMazeBuilder builder; game.CreateMaze(builder);
maze = builder.GetMaze();
return 0;
}

 

优缺点

优点:

  • 将一个复杂对象的创建过程封装起来。
  • 允许对象通过多个步骤来创建,并且可以改变过程(这和只有一个步骤的工厂模式不同)。
  • 向客户隐藏产品内部的实现。
  • 产品的实现可以被替换,因为客户只看到一个抽象的接口。

缺点:

  • 与工厂模式相比,采用生成器创建对象的模式,需要具备更多的领域知识。

参考文献:

GOF《设计模式》

《Head First设计模式》

设计模式之生成器(Builder)模式的更多相关文章

  1. 设计模式 笔记 生成器(建造者)模式 Builder

    //---------------------------15/04/08---------------------------- //builder 生成器(建造者)模式---对象创建型模式 /* ...

  2. Builder模式在Java中的应用

    在设计模式中对Builder模式的定义是用于构建复杂对象的一种模式,所构建的对象往往需要多步初始化或赋值才能完成.那么,在实际的开发过程中,我们哪些地方适合用到Builder模式呢?其中使用Build ...

  3. Builder模式(建造者模式)

    在设计模式中对Builder模式的定义是用于构建复杂对象的一种模式,所构建的对象往往需要多步初始化或赋值才能完成.那么,在实际的开发过程中,我们哪些地方适合用到Builder模式呢?其中使用Build ...

  4. Builder模式在Java中的应用(转)

    在设计模式中对Builder模式的定义是用于构建复杂对象的一种模式,所构建的对象往往需要多步初始化或赋值才能完成.那么,在实际的开发过程中,我们哪些地方适合用到Builder模式呢?其中使用Build ...

  5. 每天一个设计模式-7 生成器模式(Builder)

    每天一个设计模式-7 生成器模式(Builder) 一.实际问题 在讨论工厂方法模式的时候,提到了一个导出数据的应用框架,但是并没有涉及到导出数据的具体实现,这次通过生成器模式来简单实现导出成文本,X ...

  6. 设计模式03: Builder 生成器模式(创建型模式)

    Builder生成器模式(创建型模式) Builder模式缘起假设创建游戏中的一个房屋House设施,该房屋的构建由几个部分组成,且各个部分富于变化.如果使用最直观的设计方法,每个房屋部分的变化,都将 ...

  7. 设计模式十: 生成器模式(Builder Pattern)

    简介 生成器模式属于创建型模式的一种, 又叫建造者模式. 生成器模式涉及4个关键角色:产品(Product),抽象生成器(builder),具体生成器(ConcreteBuilder),指挥者(Dir ...

  8. 面向对象设计模式纵横谈:Builder 生成器模式(笔记记录)

    Builder模式的缘起 假设创建游戏中的一个房屋House设施,该房屋的构建由几个部分组成,且各个部分要富于变化. 如果使用最直观的设计方法,每一个房屋部分的变化,都将导致房屋构建的重新修正…… 动 ...

  9. 设计模式之构建者模式(Builder):初步理解

    构建者(Builder)设计模式(又叫生成器设计模式): 当一个类的内部数据过于复杂的时候(通常是负责持有数据的类,比如Config.VO.PO.Entity...),要创建的话可能就需要了解这个类的 ...

随机推荐

  1. window下如何使用Git上传代码到github远程服务器上(转)

    注册账户以及创建仓库 首先你得有一个github账号,没有自行注册,登录成功后应该是这样 在页面上方用户菜单上选择 "+"->New repository 创建一个新的仓库 ...

  2. linux之hdparm命令说明及其测试硬盘读写速度

    hdparm -t /dev/sda1功能说明:显示与设定硬盘的参数. 语 法:hdparm [-CfghiIqtTvyYZ] [-a <快取分区>][-A <0或1>][-c ...

  3. Sublime Text [Decode error - output not utf-8]

    改Sublime Text的python build的设置.将其编码设置为cp936. 打开Python.sublime-build文件,并添加”encoding”:”cp936″这一行,保存即可 S ...

  4. apache和tomcat公用80端口

    原理主要利用apache的代理. 第一步:修改apache的httpd.conf配置文件. 首先,要让apache支持转发也就是做tomcat的代理那么就要先启用apache的代理模块.首先我在Apa ...

  5. Java中的a++和++a的区别

    1.a++和++a的区别 a++表示先将a放在表达式中运算,然后再加1: ++a表示先将a加1,然后再放在表达式中运算 2.实例 (1)Java代码 /** * @Title:JiaJia.java ...

  6. Jquery ajaxfileupload.js结合.ashx文件实现无刷新上传

    先上几张图更直观展示一下要实现的功能,本功能主要通过Jquery ajaxfileupload.js插件结合ajaxUpFile.ashx一般应用程序处理文件实现Ajax无刷新上传功能,结合NPOI2 ...

  7. E: 未发现软件包 install_flash_player_11_linux.x86_64.tar.gz

    1 错误描述 youhaidong@youhaidong:~$ sudo apt-get install install_flash_player_11_linux.x86_64.tar.gz 正在读 ...

  8. The Eclipse executable launcher was unable to locate its companion launcher jar的解决方法

    大家都知道eclipse是免安装的,基本上解压就能用,但是笔者在解压后打开eclipse时遇到一个奇葩的问题,如题目所示. 在网上搜了半天,基本上都说的是:The Eclipse executable ...

  9. python实现简单排序算法

    算法 递归两个特点: 调用自身 有穷调用 计算规模越来越小,直至最后结束 用装饰器修饰一个递归函数时会出现问题,这个问题产生的原因是递归的函数也不停的使用装饰器.解决方法是,只让装饰器调用一次即可,那 ...

  10. OpenStack_I版 5.Nova部署

    Nova安装 创建配置存放目录,日志存放目录,执行文件目录,虚拟机目录  Nova配置修改 生成主配置文件 创建Nova数据库 同步Nova数据库 验证 Nova连接RabbitMQ配置修改  key ...