建造者模式

在GOF的《设计模式 可复用面向对象软件的基础》中是这样说的:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

这句话,似懂非懂的。一个复杂对象的创建,其通常是由很多的子对象构成;如果一个对象能够直接就创建好了,那么也不会称之为复杂对象。由于项目中需求的变化,这个复杂对象的各个部分经常会发生剧烈的变化,但是,不管怎么变化,将它们组合在一起,组成一个复杂的对象的事实是不会变的。建造者模式就提供了一种“封装机制”来将各个对象的变化隔离开,最终,组合成复杂对象的过程是不会变的。

在《大话设计模式》一书中,例举了一个很好的例子————建造小人。建造一个小人,要分为六步:头部、身体、左手、右手、左脚和右脚。与抽象工厂模式不同的是,建造者模式是在Director的控制下一步一步的构造出来的,在建造的过程中,建造者模式可以进行更精细的控制。不管人的头部、身体、左手、右手、左脚或者右脚如何变化,但是最终还是由这几部分组合在一起形成一个人,虽然是同一个建造过程,但是这个人就会有不同的表示,比如,胖子,瘦子,个高的,个低的等等。

UML图

类图如下:

时序图如下:

代码实现

/*****************************************************
* \file BuilderPatternDemo.cpp
* \date 2016/07/24 15:57
* \author ranjiewen
* \contact: ranjiewen@outlook.com *****************************************************/ #include<iostream>
using namespace std; typedef enum MANTYPETag
{
kFatMan,
kThinMa,
kNormal
}MANTYPE; class Man{
public:
void SetHead(MANTYPE type){ m_Type = type; }
void SetBody(MANTYPE type){ m_Type = type; }
void SetLeftHand(MANTYPE type){ m_Type = type; }
void SetRightHand(MANTYPE type){ m_Type = type; }
void SetLeftFoot(MANTYPE type){ m_Type = type; }
void SetRightFoot(MANTYPE type){ m_Type = type; }
void ShowMan()
{
switch (m_Type)
{
case kFatMan:
cout << "I'm a fat man!";
break;
case kThinMa:
cout << "I'm a thin man!";
break;
default:
cout << "I'm a normal man!";
break;
}
}
private:
MANTYPE m_Type;
}; class Builder{ public:
virtual void buildHead() = ;
virtual void buildBody() = ;
virtual void buildLeftHand() = ;
virtual void buildRightHand() = ;
virtual void buildLeftFoot() = ;
virtual void buildRightFoot() = ;
virtual Man* getMan() = ;
}; class FatBuilder :public Builder{
public:
FatBuilder() { m_FatMan = new Man(); }
void buildHead(){ m_FatMan->SetHead(kFatMan); }
void buildBody(){ m_FatMan->SetBody(kFatMan); }
void buildLeftHand(){ m_FatMan->SetLeftHand(kFatMan); }
void buildRightHand(){ m_FatMan->SetRightHand(kFatMan); }
void buildLeftFoot(){ m_FatMan->SetLeftFoot(kFatMan); }
void buildRightFoot(){ m_FatMan->SetRightFoot(kFatMan); }
Man* getMan(){ return m_FatMan; }
private:
Man* m_FatMan;
}; class ThinBuilder :public Builder
{
private:
Man* m_ThinMan;
ThinBuilder(){ m_ThinMan = new Man(); }
void buildHead(){ m_ThinMan->SetHead(kThinMa); }
void buildBody(){ m_ThinMan->SetBody(kThinMa); }
void buildLeftHand(){m_ThinMan->SetLeftHand(kThinMa); }
void buildRightHand(){ m_ThinMan->SetRightHand(kThinMa); }
void buildLeftFoot(){ m_ThinMan->SetLeftFoot(kThinMa); }
void buildRightFoot(){ m_ThinMan->SetRightFoot(kThinMa); }
Man* getMan(){ return m_ThinMan; }
}; class Director{ private:
Builder* m_Builder;
public:
Director(Builder *builder):m_Builder(builder){}
void CreateMan();
}; void Director::CreateMan(){
m_Builder->buildHead();
m_Builder->buildBody();
m_Builder->buildLeftHand();
m_Builder->buildRightHand();
m_Builder->buildLeftFoot();
m_Builder->buildRightFoot();
} int main(){
Builder* builderObj = new FatBuilder();
Director directorObj(builderObj);
directorObj.CreateMan(); Man* manObj = builderObj->getMan();
if (manObj!=nullptr)
{
manObj->ShowMan();
} delete manObj;
manObj = nullptr; delete builderObj;
builderObj = nullptr;
return ;
}

上面这个例子比较杂,但是也是建造者模式的应用。下面这个例子是建造者最一般,最简单的实现方法:

#include <iostream>
#include <vector>
using namespace std; class Builder; // Product
class Product
{
public:
void AddPart(const char *info) { m_PartInfoVec.push_back(info); }
void ShowProduct()
{
for (std::vector<const char *>::iterator item = m_PartInfoVec.begin();
item != m_PartInfoVec.end(); ++item)
{
cout<<*item<<endl;
}
} private:
std::vector<const char *> m_PartInfoVec;
}; // Builder
class Builder
{
public:
virtual void BuildPartA() {}
virtual void BuildPartB() {}
virtual Product *GetProduct() { return NULL; }
}; // ConcreteBuilder
class ConcreteBuilder : public Builder
{
public:
ConcreteBuilder() { m_Product = new Product(); }
void BuildPartA()
{
m_Product->AddPart("PartA completed");
} void BuildPartB()
{
m_Product->AddPart("PartB completed");
} Product *GetProduct() { return m_Product; } private:
Product *m_Product;
}; // Director
class Director
{
public:
Director(Builder *builder) { m_Builder = builder; }
void CreateProduct()
{
m_Builder->BuildPartA();
m_Builder->BuildPartB();
} private:
Builder *m_Builder;
}; // main
int main()
{
Builder *builderObj = new ConcreteBuilder();
Director directorObj(builderObj);
directorObj.CreateProduct();
Product *productObj = builderObj->GetProduct();
if (productObj == NULL)
{
return ;
}
productObj->ShowProduct(); delete productObj;
productObj = NULL; // 谢谢宾零同学的review
delete builderObj;
builderObj = NULL;
}

使用要点

  1. 建造者模式生成的对象有复杂的内部结构,将分步骤的去构建一个复杂的对象,分多少步是确定的,而每一步的实现是不同的,可能经常发生变化;
  2. 在上面的例子中,我们都看到了最终生成的Man和Product都没有抽象类,这又导出建造者适用的一种情况,当需要创建复杂对象的过程中,复杂对象没有多少共同的特点,很难抽象出来时,而复杂对象的组装又有一定的相似点时,建造者模式就可以发挥出作用。简单的说,可能使用了建造者模式,最终建造的对象可能没有多大的关系,关于这一点,阅读《设计模式 可复用面向对象软件的基础》中的建造者模式时是最有体会的。

总结

一个复杂对象是由多个部件组成的,建造者模式是把复杂对象的创建和部件的创建分别开来,分别用Builder类和Director类来表示。用Director构建最后的复杂对象,而在上面Builder接口中封装的是如何创建一个个部件(复杂对象是由这些部件组成的),也就是说,Director负责如何将部件最后组装成产品。这样建造者模式就让设计和实现解耦了。

刚开始接触建造者模式的时候,最容易把建造者和抽象工厂模式混淆了。由于而这都属于创建型的设计模式,所以二者之间是有公共点的,但是建造者模式注重于对象组合,即不同的小对象组成一个整体的复杂大对象,而抽象工厂模式针对于接口编程,只是对外提供创建对象的工厂接口,不负责对象之后的处理。

建造者模式,是一个比较复杂,不容易权衡的设计模式。大家应该更多的阅读开源代码,理解他人是如何使用该模式的。从实际的应用中学习设计模式。

C++设计模式——建造者模式的更多相关文章

  1. 3.java设计模式-建造者模式

    Java设计模式-建造者模式 在<JAVA与模式>一书中开头是这样描述建造(Builder)模式的: 建造模式是对象的创建模式.建造模式可以将一个产品的内部表象(internal repr ...

  2. 设计模式—建造者模式(Builder)

    title: 设计模式-建造者模式 建造者模式(Builder)是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节.建造者模式属于对 ...

  3. C#设计模式-建造者模式

    在软件系统中,有时需要创建一个复杂对象,并且这个复杂对象由其各部分子对象通过一定的步骤组合而成. 例如一个采购系统中,如果需要采购员去采购一批电脑时,在这个实际需求中,电脑就是一个复杂的对象,它是由C ...

  4. [Head First设计模式]山西面馆中的设计模式——建造者模式

    系列文章 [Head First设计模式]山西面馆中的设计模式——装饰者模式 [Head First设计模式]山西面馆中的设计模式——观察者模式 引言 将学习融入生活中,是件很happy的事情,不会感 ...

  5. 说说设计模式~建造者模式(Builder)

    返回目录 建造者模式是我的"设计模式"里创建型模式里的最后一篇,这种模式在实现中,很多架构都用到了,如MVC,MVP,MVVM,它们都是有建造者模式的精髓的,即,创建与表现分享,我 ...

  6. Python设计模式——建造者模式

    需求,画人物,要求画一个人的头,左手,右手,左脚,右脚和身体,画一个瘦子,一个胖子 不使用设计模式 #encoding=utf-8 __author__ = 'kevinlu1010@qq.com' ...

  7. 我的Java设计模式-建造者模式

    在未上大学之前,一直有个梦想"I have a dream!",就是能成为一位汽车工程师,一直幻想着开着自己设计的汽车飞奔在公路上,迷倒了万千少女.咳咳~~虽然现在没实现我的dre ...

  8. 设计模式 | 建造者模式/生成器模式(builder)

    定义: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 结构:(书中图,侵删) 一个产品类 一个指定产品各个部件的抽象创建接口 若干个实现了各个部件的具体实现的创建类 一个 ...

  9. java设计模式——建造者模式

    一. 定义与类型 定义:将一个复杂对象的构建与它的表示分离,使用同样的构建过程可以创建不同的表示 用户只需制定需要建造的类型就可以得到它们,建造过程以及细节不需要知道 类型:创建型 建造者模式与工厂模 ...

随机推荐

  1. iOS开发--多线程

    前面在<Bison眼中的iOS开发多线程是这样的(二)>一文中讲完了多线程的NSThread,不难发现这种方式的多线程实现起来非常的复杂,为了简化多线程的开发,iOS提供了GCD来实现多线 ...

  2. [Unity菜鸟] 产生各不相同的随机数

    1. 网上很多方法都说用时间种子来解决,但是在极短的时间内,这种方法没效 Random r = new Random(DateTime.Now.Millisecond); Random Counter ...

  3. PCB工艺镀金(电金)和沉金(化金)的区别

    1.镀金和沉金的别名分别是什么?   镀金:硬金,电金(镀金也就是电金) 沉金:软金,化金 (沉金也就是化金) 2.别名的由来:  镀金:通过电镀的方式,使金粒子附着到pcb板上,所以叫电金,因为附着 ...

  4. 解决不安装VC运行库(VC2005,VC2008),程序运行出错的方法

    因为VS2005以后程序采用了manifest的生成方式,所以发布的时候要和运行库一起发布.但是我们平时开发和发布的时候如果都要客户安装运行库,那就不太方便了.你可以Microsoft下载:http: ...

  5. 转Struts 权限控制

    权限最核心的是业务逻辑,具体用什么技术来实现就简单得多. 通常:用户与角色建立多对多关系,角色与业务模块构成多对多关系,权限管理在后者关系中. 对权限的拦截,如果系统请求量大,可以用Struts2拦截 ...

  6. Struts2 Package

    package 元素的所有属性及对应功能: Attribute Required Description name yes key to for other packages to reference ...

  7. Java API —— 递归

    1.方法定义中调用方法本身的现象 2.递归注意实现         1) 要有出口,否则就是死递归         2) 次数不能太多,否则就内存溢出         3) 构造方法不能递归使用 3. ...

  8. Junit使用教程(四)

    一.会用Spring测试套件的好处 在开发基于Spring的应用时,如果你还直接使用Junit进行单元测试,那你就错过了Spring为我们所提供的饕餮大餐了.使用Junit直接进行单元测试有以下四大不 ...

  9. struts1,struts2,springMVC终极对比

    最近做项目用到了struts2,之前一直是用struts1和springMVC.感觉到了struts2从很大程度上和这两个还是有很大区别的,所以今天搜集了些资料,给他们做一下对比. Struts1官方 ...

  10. 对github中项目进行更新

    进入本地仓库文件夹,我的仓库名是tufujiegit,然后 进入 git  clone  接着将先前记录下来的地址复制到后面,回车 将下载github中该仓库的所有文件及文件夹,包括.git文件夹在内 ...