详解C++设计模式编程中建造者模式的实现

所属分类: 软件编程 / C 语言 阅读数: 74
收藏 0 赞 0 分享

建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。这是建造者模式的标准表达,不过看着让人迷惑,什么叫构建和表示的分离?一个对象使用构造函数构造之后不就固定了,只有通过它方法来改变它的属性吗?而且还要同样的构建过程搞出不同的表示,怎么可能呢?多写几个构造函数?

其实多写几个构造函数,根据不同参数设置对象不同的属性,也可以达到这样的效果,只是这样就非常麻烦了,每次要增加一种表示就要添加一个构造函数,将来构造函数会多得连自己都不记得了,这违背了开放-封闭的原则。

要不就只能设计几个set函数,每次属性不一样了,我就构造一个对象,然后用set函数改变对象的属性。这样也可以达到效果。只是代码就会非常冗余了,每个要用到这个对象的地方,都要写上好几句语句,一旦对象有点什么变化,还得到处都改一遍,这样就很容易出错,以后别人看着这种神逻辑和神代码估计也会崩溃了。而且这也违背了依赖倒转的原则。

于是大神们就开始想了,不能加很多构造函数,也不能直接用一堆set函数,然后发现,有些对象的构建是固定的几个步骤的,就像一条流水线一样,任何的产品都是通过每一个固定的步骤拼凑出来的。例如说一部手机,先放主板,再放屏幕,再放电池,再放外壳,贴个膜就能卖几千了,每次推出新产品,就换个更好的主板,换个大点的屏幕,再整个大容量电池,贴个超牛B的高透膜,又能卖出个新价钱。就是说,这些步骤都没有变,变的只是每个部分的东西。

这就是大神的厉害之处了,透过现象看本质,基本有变的,有不变的,那敢情好,面向对象的一个重要指导思想就是,封装隔离变化的,留出不变的。于是他们就用一个Builder类把步骤中的每个部分封装起来,这个类的主要作用就是生产每个部件,再抽象一下提升高度,这样就依赖倒转了,这样每次只需要添加一个类,这个类还是这几个部分,只是内部的实现已经不一样了,这样就满足了开放-封闭的原则了。但还是有一个问题,光有Builder类还不行,虽然产品的每个部分都有对应的函数,但是用起来的话,还是跟前面说的set函数一样,一用就要使用一大堆函数,也就是这变的东西是封装起来了,但这不变的东西还没留出来。这时,就添加一个Director类,这个类就是专门规定组装产品的步骤的,这样只要告诉Director使用哪个Builder,就能生产出不同的产品,对于客户端来说,只看到用了Director的一个construct函数,甚是方便。

再反过来看建造者模式的定义,构建指的就是生产一个产品的步骤,表示就是每个产品部分的具体实现,通过Director封装步骤,通过Builder封装产品部分的实现,再把他两隔离开,就能隔离变的,留出不变的供客户端使用。

20163892712891.jpg (794×468)

图中可以看到,Product是必须要知道,没有抽象,但是这个产品却可以由不同的部分组合而成。Director里的construct也是固定,没有抽象出来,如果要更改步骤,也要添加一个函数,或者再添一个Diector,所以建造者模式一般应用于步骤不会发生大的变化,而产品会发生大变化的情况。

常用的场景
C#中的StringBuilder就是一个建造者的例子,但只是一个建造者,还缺一个Director,不能算一个完整的建造者模式。建造者模式一般应用于构建产品的步骤(也可以称为算法)不变,而每个步骤的具体实现又剧烈变化的情况。

优点
1.隔离了构建的步骤和具体的实现,为产品的具体实现提供了灵活度。

2.封装和抽象了每个步骤的实现,实现了依赖倒转原则。

3.封装了具体的步骤,减少了代码的冗余。

缺点
1.要求构建产品的步骤(算法)是不能剧烈变化的,最好是不变的,这样就影响了灵活度。

实例

#include "stdafx.h" 
#include <stdlib.h> 
#include <iostream> 
using namespace std; 
  
//抽象类,用来安排创建人的具体流程,其他类必须遵循这个流程,但是可以自己具体实现 
class CPersonBuilder 
{ 
public: 
 virtual void BuildHead()=0; 
 virtual void BuildBody()=0; 
 virtual void BuildArmLeft()=0; 
 virtual void BuildArmRight()=0; 
 virtual void BuildLegLeft()=0; 
 virtual void BuildLegRight()=0; 
}; 
  
//创建瘦子的类 
class CThinPersonBuilder:public CPersonBuilder 
{ 
public: 
 CThinPersonBuilder() 
 { 
 cout<<"is creating thin person "<<endl<<endl; 
 } 
 ~CThinPersonBuilder() 
 { 
 cout<<"is finished for thin person"<<endl<<endl; 
 } 
public: 
 void BuildHead() 
 { 
 cout<<"BuildHead"<<endl; 
 } 
 void BuildBody() 
 { 
 cout<<"BuildBody(thin)"<<endl; 
 } 
 void BuildArmLeft() 
 { 
 cout<<"BuildArmLeft"<<endl; 
 } 
 void BuildArmRight() 
 { 
 cout<<"BuildArmRight"<<endl; 
 } 
 void BuildLegLeft() 
 { 
 cout<<"BuildLegLeft"<<endl; 
 } 
 void BuildLegRight() 
 { 
 cout<<"BuildLegRight"<<endl; 
 } 
}; 
 //创建胖子的类 
class CFatPersonBuilder:public CPersonBuilder 
{ 
public: 
 CFatPersonBuilder() 
 { 
 cout<<"is creating fat person"<<endl; 
 } 
 ~CFatPersonBuilder() 
 { 
 cout<<"is finished for fat person"<<endl; 
 } 
public: 
 void BuildHead() 
 { 
 cout<<"BuildHead"<<endl; 
 } 
 void BuildBody() 
 { 
 cout<<"BuildBody(Fat)"<<endl; 
 } 
 void BuildArmLeft() 
 { 
 cout<<"BuildArmLeft"<<endl; 
 } 
 void BuildArmRight() 
 { 
 cout<<"BuildArmRight"<<endl; 
 } 
 void BuildLegLeft() 
 { 
 cout<<"BuildLegLeft"<<endl; 
 } 
 void BuildLegRight() 
 { 
 cout<<"BuildLegRight"<<endl; 
 } 
}; 
  
//指挥者类,用来指挥创建的人是瘦子还是胖子 
class CPersonDirector 
{ 
public: 
 CPersonDirector(CPersonBuilder *p) 
 { 
 this->m_p=p; 
 } 
 const void CreatePerson(void) const 
 { 
 m_p->BuildHead(); 
 m_p->BuildBody(); 
 m_p->BuildArmLeft(); 
 m_p->BuildArmRight(); 
 m_p->BuildLegLeft(); 
 m_p->BuildLegRight(); 
 } 
private: 
 CPersonBuilder *m_p; 
}; 
  
  
  
 
int _tmain(int argc, _TCHAR* argv[]) 
{ 
 cout<<"---------建造者模式测试案例------------------------"<<endl<<endl; 
  
 CThinPersonBuilder *p_tp=new CThinPersonBuilder(); 
 CPersonDirector *p_dtp=new CPersonDirector(p_tp); 
 p_dtp->CreatePerson(); 
 delete p_tp; 
 delete p_dtp; 
 p_tp=NULL; 
 p_dtp=NULL; 
 cout<<endl<<endl; 
 
 CFatPersonBuilder *p_fp=new CFatPersonBuilder(); 
 CPersonDirector *p_dfp=new CPersonDirector(p_fp); 
 p_dfp->CreatePerson(); 
 delete p_fp; 
 delete p_dfp; 
 p_fp=NULL; 
 p_dfp=NULL; 
 system("pause"); 
 return 0; 
} 

20163892815043.png (675×418)

更多精彩内容其他人还在看

用标准c++实现string与各种类型之间的转换

这个类在头文件中定义, < sstream>库定义了三种类:istringstream、ostringstream和stringstream,分别用来进行流的输入、输出和输入输出操作。另外,每个类都有一个对应的宽字符集版本
收藏 0 赞 0 分享

C++如何通过ostringstream实现任意类型转string

再使用整型转string的时候感觉有点棘手,因为itoa不是标准C里面的,而且即便是有itoa,其他类型转string不是很方便。后来去网上找了一下,发现有一个好方法
收藏 0 赞 0 分享

C/C++指针小结

要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区
收藏 0 赞 0 分享

C++ 类的静态成员深入解析

在C++中类的静态成员变量和静态成员函数是个容易出错的地方,本文先通过几个例子来总结静态成员变量和成员函数使用规则,再给出一个实例来加深印象
收藏 0 赞 0 分享

C++类的静态成员初始化详细讲解

通常静态数据成员在类声明中声明,在包含类方法的文件中初始化.初始化时使用作用域操作符来指出静态成员所属的类.但如果静态成员是整型或是枚举型const,则可以在类声明中初始化
收藏 0 赞 0 分享

C++类静态成员与类静态成员函数详解

静态成员不可在类体内进行赋值,因为它是被所有该类的对象所共享的。你在一个对象里给它赋值,其他对象里的该成员也会发生变化。为了避免混乱,所以不可在类体内进行赋值
收藏 0 赞 0 分享

C++中的friend友元函数详细解析

友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类。友元函数的特点是能够访问类中的私有成员的非成员函数。友元函数从语法上看,它与普通函数一样,即在定义上和调用上与普通函数一样
收藏 0 赞 0 分享

static全局变量与普通的全局变量的区别详细解析

以下是对static全局变量与普通的全局变量的区别进行了详细的分析介绍,需要的朋友可以过来参考下,希望对大家有所帮助
收藏 0 赞 0 分享

C++ explicit关键字的应用方法详细讲解

C++ explicit关键字用来修饰类的构造函数,表明该构造函数是显式的,既然有"显式"那么必然就有"隐式",那么什么是显示而什么又是隐式的呢?下面就让我们一起来看看这方面的知识吧
收藏 0 赞 0 分享

教你5分钟轻松搞定内存字节对齐

随便google一下,人家就可以跟你解释的,一大堆的道理,我们没怎么多时间,讨论为何要对齐.直入主题,怎么判断内存对齐规则,sizeof的结果怎么来的,请牢记以下3条原则
收藏 0 赞 0 分享
查看更多