C++设计模式之建造者模式
2016-03-05 03:02
671 查看
C++设计模式之建造者模式
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。C设计模式之建造者模式
一缘由
二实现
三代码分析
四总结
一、缘由
当我们在构造一个窗口控件的时候,往往包含三个方面的初始化工作:UI初始化
动画初始化
信号槽初始化
这样我们就可以构造好一个窗口控件了,我们可以看以下类图:
乍一看该实现并没有什么问题
CenterWidget类在其构造函数中调用了
initUi、
initAnimation、
initSlot三个私有成员函数分别进行Ui,动画,信号槽的初始化。可是当我们进行一个新的
CenterWidget构造时,要求动画效果改变,这时候需要怎么做呢?修改
initAnimation成员函数?作为一名优秀的程序员,提到修改类的时候就应该警惕,这个设计明显违反了开闭原则。这时候我们遇到到一个问题:构造的接口是固定的,构造的顺序是固定的,而要求构造的内容变化。
我们直觉上,这个问题的解就是:
initUi,
initAnimation,
initSlot三个成员函数必须为虚函数。在需要动画、Ui或者信号槽发生变化的时候,只要需要添加新的子类,重写其中某个函数需要变化即可。可是,当这三个成员函数都成为虚函数之后,就不可能在构造函数中调用,因为
在一个类构造期间,vtable还没有初始化完成,虚函数机制不会正确工作。
于是我们增加了一个
Init方法,在对象被构造出来之后调用之。此时类图如下:
对于这个类图来说,解决了开闭原则的问题,可是新的问题出现了,客户端需要调用
Init,有违反了接口隔离的和单一职责原则之嫌疑,为何这么说呢?
客户端的想法是:
我想要一个
CenterWidget实例
然后将
CenterWidget显示出来
而
CenterWidget提供的却是:
给你一个
CenterWidget实例
然后调用
Init接口初始化
然后将
CenterWidget显示出来
对于客户端来说,它需要依赖一个它不需要的接口
Init,这个
Init不是客户端所要求的,反而更象是客户端需要
CenterWidget作为主窗口的逻辑功能而强行搭配的一个接口(注:接口隔离原则指的是客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。)。其次,作为主窗口类的
CenterWidget需要额外负责管理复杂的构造逻辑。这倒不是什么严重的问题,很多时候这种解决方法都是行之有效的,不过我们提出一种更为优雅的解决方法。
二、实现
为了解决上节所述的问题,我们将整个CenterWidget构造职责抽取出来由单独的一个
Builder类承担。这样将
initUi、
initAnimation、
initSlot函数转移到
Builder类中。由
Builder类构造出来
CenterWidget实例,另外,有时候对
initUi、
initAnimation、
initSlot三个函数调用次序有所要求,引入一个
Director类专门管理对
initUi、
initAnimation、
initSlot的调用次序,指挥
Builder的工作,这就是建造者模式。建造者模式的示意图和使用了建造者模式的
CenterWidget建造方案如下图所示:
建造者模式
使用建造者模式的`CenterWidget`方案
三、代码分析
下面给出CenterWidget方案的示例代码
[code]#include <string> #include <iostream> using std::string; class CenterWidget { private: string Ui; string Animation; string Slot; public: virtual ~CenterWidget (){}; void setUi(const string& x){ Ui = x; } void setAnimation(const string& x){ Animation = x; } void setSlot(const string& x){ Slot = x; } void show(){ std::cout << "Ui = " << Ui << " Animation = " << Animation << " Slot = " << Slot << std::endl; } }; class CenterWidgetBuilder { public: virtual ~CenterWidgetBuilder(){} virtual void initUi() = 0; virtual void initAnimation() = 0; virtual void initSlot() = 0; virtual CenterWidget *getResult() = 0; }; class ConcreteCenterWidgetBuilderA : public CenterWidgetBuilder { private: CenterWidget *curWidget; public: ConcreteCenterWidgetBuilderA():curWidget(new CenterWidget){} virtual ~ConcreteCenterWidgetBuilderA(){ delete curWidget; }; virtual void initUi(){ curWidget->setUi("Q Ui"); } virtual void initAnimation(){ curWidget->setAnimation("Biu~Biu~Biu~"); } virtual void initSlot(){ curWidget->setSlot("connected to your heart"); } CenterWidget *getResult(){ return curWidget; }; }; class ConcreteCenterWidgetBuilderB : public CenterWidgetBuilder { private: CenterWidget *curWidget; public: ConcreteCenterWidgetBuilderB():curWidget(new CenterWidget){} virtual ~ConcreteCenterWidgetBuilderB(){ delete curWidget; }; virtual void initUi(){ curWidget->setUi("Q Ui"); } virtual void initAnimation(){ curWidget->setAnimation("Boom~Boom~Boom~"); } virtual void initSlot(){ curWidget->setSlot("connected to your heart"); } CenterWidget *getResult(){ return curWidget; }; }; class Dirctor { private: CenterWidgetBuilder *Builder; public: Dirctor (CenterWidgetBuilder* builder):Builder(builder){}; virtual ~Dirctor(){delete Builder;} void Construct(){ Builder->initUi(); Builder->initAnimation(); Builder->initSlot(); } }; int main(void) { ConcreteCenterWidgetBuilderA *builderA = new ConcreteCenterWidgetBuilderA; Dirctor *directorA = new Dirctor(builderA); directorA->Construct(); builderA->getResult()->show(); ConcreteCenterWidgetBuilderB *builderB = new ConcreteCenterWidgetBuilderB; Dirctor *directorB = new Dirctor(builderB); directorB->Construct(); builderB->getResult()->show(); } 运行结果: Ui = Q Ui Animation = Biu~Biu~Biu~ Slot = connected to your heart Ui = Q Ui Animation = Boom~Boom~Boom~ Slot = connected to your heart
ConcreteCenterWidgetBuilderB和ConcreteCenterWidgetBuilderA中有重复代码,为了方便代码复用,我们可以使用ConcreteCenterWidgetBuilderB继承ConcreteCenterWidgetBuilderA,然后重写需要变化的initAnimation即可,代码修改如下:
[code] class ConcreteCenterWidgetBuilderA : public CenterWidgetBuilder { protected: //为了子类能访问之,改为protected CenterWidget *curWidget; public: ConcreteCenterWidgetBuilderA():curWidget(new CenterWidget){} virtual ~ConcreteCenterWidgetBuilderA(){ delete curWidget; }; virtual void initUi(){ curWidget->setUi("Q Ui"); } virtual void initAnimation(){ curWidget->setAnimation("Biu~Biu~Biu~"); } virtual void initSlot(){ curWidget->setSlot("connected to your heart"); } CenterWidget *getResult(){ return curWidget; }; }; class ConcreteCenterWidgetBuilderB : public ConcreteCenterWidgetBuilderA { public: ConcreteCenterWidgetBuilderB(){} virtual ~ConcreteCenterWidgetBuilderB(){ }; //只重写需要改变的部分 virtual void initAnimation(){ curWidget->setAnimation("Boom~Boom~Boom~"); } }; 运行结果: Ui = Q Ui Animation = Biu~Biu~Biu~ Slot = connected to your heart Ui = Q Ui Animation = Boom~Boom~Boom~ Slot = connected to your heart
四、总结
建造者模式的主要优点 :将产品本身和产品的创建过程解耦,使得不同的创建过程创建出不同的实例
可以很方便地增加新的建造者,实现新的产品实例的创建,符合开闭原则
建造者模式的主要缺点 :
建造者模式只能创建具有许多共同点的产品,组成成分相似
如果产品内部组成复杂多变,将需要定义大量的建造者类,使得系统复杂化
使用场景 :
需要创建的产品具有多个组成部分且内部构造复杂
需要指定产品的创建顺序
对象创建的过程需要独立于该类
相关文章推荐
- 拷贝构造函数的参数为什么必须使用引用类型
- C++获取随机数的办法
- DSP 2812: 使用C++实现的SCI从动站程序框架
- C/C++ 如何遍历文件夹中最大的那一个文件
- 异常安全,RAII与C++11
- C/C++中的'\0'
- c++中一个关于模板的小例子
- C++中构造函数能调用虚函数吗?(转)
- C/C++调用java---JNI常用函数
- DSP 2812: 使用C++实现的SCI主动站程序框架
- C/C++中的回车与换行
- C语言——源码、反码、补码
- HDU 1062 Text Reverse(水题)
- 【C语言位运算的应用】如何按bit位翻转一个无符号整型
- C++类模板使用详解
- C++内存管理
- c++ primer 第五版的Sales_data类
- jnaerator命令行生成bridj的包(java调用c++动态链接库)
- C、C++中的隐式转换和显示转换
- C语言中一些对文件操作的函数总结