意图

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

适用性

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

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. 后台模拟请求 HttpHelper 序列化 MD5 时间戳

    使用场景 C# 服务器后台调用其他服务器并处理 场景 /// <summary> /// 获取用户信息 /// </summary> /// <param name=&q ...

  2. Oracle trunc() 函数处理数字、日期的整理

    --Oracle trunc()函数的用法/**************日期********************/1.select trunc(sysdate) from dual --2013- ...

  3. 移动端-手机端-日历选择控件(支持Zepto和JQuery)

    一. 效果图 二. 功能说明 1. 支持切换年份,月份. 2. 支持点击选中日期,也可以点击确定选择日期. 三. 使用方法 1. 添加Input 在你的页面中添加Input输入框.可以再html里,也 ...

  4. SQL Server使用侦听器IP访问时遇到"The target principal name is incorrect. Cannot generate SSPI context"

    在测试SQL Server 2016 Always On时,在创建侦听器后,在客户端使用SSMS, 可以用侦听器名称访问Always On集群,但是使用侦听器IP访问时遇到"The targ ...

  5. 用DDK开发的9054驱动 .

    和S5933比较起来,开发PLX9054比较不幸,可能是第一次开发PCI的缘故吧.因为,很多PCI的例子都是对S5933,就连微软出版的<Programming the Microsoft Wi ...

  6. JBoss启动项目报错

    具体报错如下: WARNING: -logmodule is deprecated. Please use the system property 'java.util.logging.manager ...

  7. JavaScript中的indexOf

    JavaScript中的indexOf 1.JavaScript中利用indexOf拼接字符串 <%@ page language="java" import="j ...

  8. Java中用正则表达式找出数字

    Java中用正则表达式找出数字 1.题目    String str = "fjd789klsd908434jk#$$%%^38488545",从中找出78990843438488 ...

  9. Excel 转 vCard格式、CSV格式

    Excel 转vCard格式(常用于Gmail, Yahoo, 163等).CSV格式(常用于Outlook, Foxmail等) 最近公司邮件通讯录需要更新,我就将原来的通讯录给删除了,准备重新导入 ...

  10. C# 图解教程 第一章 C#和.NET框架

    C#和.NET框架 在.NET之前.NET时代 .NET框架的组成大大改进的编程环境 编译成CIL编译成本机代码并执行CLR Common Language RuntimeCLI Common Lan ...