情景分析
还是让我们生产车模 , 奔驰和宝马, . 这回额外增加了一个新的需求 , 那就是汽车的启动 , 停止 , 喇叭声音 , 引擎声音都是由客户自己控制 , 他想什么顺序就什么顺序 . 那我们还是按照模板类的方式完成(这里可以直接理解成一个汽车的模型有上述四种功能即可, 不用翻代码) . 然后 , 我们来模拟一下XX公司的要求 : 生产一个奔驰模型 , 要求跑的时候 , 先发动引擎 , 然后在挂挡启动 , 然后停下 , 不需要喇叭 . 这个很容器满足 , 通过场景类(Java , 或者可以理解成主函数)实现该需求 .
/*
*客户告诉XX公司 , 我们要这样一个模型, 然后XX公司就告诉我老大
*说是要这样一个模型 , 这样一个顺序 , 然后我就来制造
*/
//客户要求 , run() [run()是一个测试车模所有功能的成员方法]的时候先发动引擎
//启动起来
//开了一段就停下
//我们把这个顺序赋予奔驰车
benz.run();
我们组装了这样的一辆汽车,满足了xx公司的需求 . 但是想想我们的需求,汽车的动作执行顺序是要能够随意调整的 . 我们只满足了一个需求,还有下一个需求呀,然后是第二个宝马模型,只要启动、停止,其他的什么都不要;第三个模型,先喇叭,然后启动,然后停止;第四…直到把你逼疯为止,那怎么办? 我们就一个一个地来写场景类满足吗? 不可能的,那我们要想办法来解决这个问题,有了! 我们为每种模型产品模型定义一个建造者,你要啥顺序直接告诉建造者,由建造者来建造 .
建造者模式的定义
定义 : 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示 .
建造者模式的通用类图 :
在建造者模式中 , 有如下四个角色 :
- (Product)产品类 : 一般是一个较为复杂的对象,也就是说创建对象的过程比较复杂,一般会有比较多的代码量 . 在本类图中,产品类是一个具体的类,而非抽象类 . 实际编程中,产品类可以是由一个抽象类与它的不同实现组成,也可以是由多个抽象类与他们的实现组成 . (例子中的BenzModel就属于产品类)
- (Builder)抽象建造者 : 引入抽象建造者的目的,是为了将建造的具体过程交与它的子类来实现 . 这样更容易扩展 .一般至少会有两个抽象方法,一个用来建造产品,一个是用来返回产品 .
- (ConcreteBuilder)具体建造者 : 实现抽象类的所有未实现的方法,具体来说一般是两项任务:组建产品;返回组建好的产品 .
- (Director)导演类 : 负责调用适当的建造者来组建产品,导演类一般不与产品类发生依赖关系,与导演类直接交互的是建造者类 . 一般来说,导演类被用来封装程序中易变的部分 .
代码实现 :
#include<iostream>
#include<vector>
#include<string>
//#include<vld.h>
using namespace std;
//建造者模式
#if 0
//汽车模板
class CarModel
{
protected:
virtual void start() = 0;
//能发动 , 还要能停下来 , 那才是真本事
virtual void stop() = 0;
//按喇叭会出声音 , 是滴滴叫 , 还是哔哔叫
virtual void alarm() = 0;
//引擎会轰隆隆地响 , 不响那是假的
virtual void engineBoom() = 0;
public:
void setSequence(vector<string> _sequence)
{
sequeue = _sequence;
}
//那模型应该会跑吧 , 别管是人推 , 还是电力驱动 , 总之要会跑
void run()
{
int len = sequeue.size();
for(int i = 0 ;i < len ;++i)
{
if(sequeue[i] == "start")
{
start();
}
else if(sequeue[i] == "stop")
{
stop();
}
else if(sequeue[i] == "alarm")
{
alarm();
}
else if(sequeue[i] == "engineBoom")
{
engineBoom();
}
}
}
private:
vector<string> sequeue;
};
//产品类 , 奔驰产品
class BenzModel : public CarModel
{
//独立业务处理
public:
void start() { cout<<"奔驰车跑起来是这个样子的..."<<endl; }
void stop() { cout<<"奔驰车应该这样停车..."<<endl; }
void alarm() { cout<<"奔驰车的喇叭声音是这个样子的..."<<endl; }
void engineBoom() { cout<<"奔驰车的引擎声音是这样的..."<<endl; }
};
//抽象建造者
class CarBuilder
{
//设置产品的不同部分 , 以获得不同的产品
//建造产品
public:
virtual void setSequence(vector<string> sequence) = 0;
virtual CarModel* getCarModel() = 0;
};
//具体建造者 , 奔驰车建造者
class BenzBuilder : public CarBuilder
{
//设置产品的零件
//产品类内的逻辑处理
//组建一个产品
public:
BenzBuilder()
{
benz = new BenzModel();
}
CarModel* getCarModel()
{
return benz;
}
void setSequence(vector<string> _sequence)
{
benz->setSequence(_sequence);
}
~BenzBuilder()
{
delete benz;
}
private:
BenzModel* benz;
};
//导演类
class Director
{
//构建不同的产品
//设置不同的零件 , 生产不同的产品
public:
Director(BenzBuilder* benz)
{
benzBuilder = benz;
}
/*
*A型奔驰 , 先发动引擎 , 然后启动 , 然后停止 , 没有喇叭
*/
void getABenzModel()
{
//清空容器 , 防止意想不到情况
sequence.clear();
sequence.push_back("engineBoom");
sequence.push_back("start");
sequence.push_back("stop");
benzBuilder->setSequence(sequence);
//得到benz* , 调用run()方法
benzBuilder->getCarModel()->run();
}
/*
*B型奔驰 , 先按下喇叭 , 然后发动引擎 , 然后启动 , 最后停止
*/
void getBBenzModel()
{
//清空容器 , 防止意想不到情况
sequence.clear();
sequence.push_back("alarm");
sequence.push_back("engineBoom");
sequence.push_back("start");
sequence.push_back("stop");
benzBuilder->setSequence(sequence);
//得到benz* , 调用run()方法
benzBuilder->getCarModel()->run();
}
private:
vector<string> sequence;
BenzBuilder* benzBuilder;
};
int main()
{
BenzBuilder* benz = new BenzBuilder();
Director director(benz);
director.getABenzModel();
cout<<"============================="<<endl;
director.getBBenzModel();
delete benz;
return 0;
}
#endif
运行结果 :
建造者模式的应用
建造者模式的优点
- 封装性
使用建造者模式可以使客户端不必知道产品内部组成的细节 . 如例子中我们就不需要关心 每一个具体的模型内部是如何实现的 , 产生的对象类型就是CarModel . - 建造者独立 , 容器扩展
- 便于控制细节风险
由于具体的建造者是独立的 , 因此可以对建造过程逐步细化 , 而不对其他的模块产生任何影响 .
建造者模式的使用场景
- 相同的方法,不同的执行顺序,产生不同的事件结果时,可以采用建造者模式 .
- 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时,则可以
使用该模式 . - 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造
者模式非常合适 . - 在对象创建过程中会使用到系统中的一些其他对象,这些对象在产品对象的创建过程中
不易得到时,也可以采用建造者模式封装该对象的创建过程 . 该种场景只能是一个补偿方法,因为一个对象不容易获得,而在设计阶段竟然没有发觉,而要通过创建者模式柔化创建过程,本身已经违反设计的最初目标 .
建造者模式的注意事项
建造者模式关注的是零件类型和装配工艺(顺序),这是它与工厂方法模式最大不同的地方,虽然同为创建类模式,但是注重点不同 .
建造者模式的扩展
已经不用扩展了 , 因为在例子中已经对建造者模式进行了扩展 , 引入了模板方法模式 . 请注意,建造者模式中还有一个角色没有说明,就是零件,建造者怎么去建造一个对象? 是零件的组装,组装顺序不同对象效能也不同,这才是建造者模式要表达的核心意义,而怎么才能更好地达到这种效果呢? 引人模板方法模式是一个非常简单而有效的办法 .
编者将之前的代码进行完善 , 又加入一个宝马车模的建造 , 大部分的代码都是相似的 , 在导演类中稍有不同 . 算是一个加深理解吧 . 代码如下 :
#include<iostream>
#include<vector>
#include<string>
//#include<vld.h>
using namespace std;
//建造者模式(完整例子)
#if 0
//汽车模板
class CarModel
{
protected:
virtual void start() = 0;
//能发动 , 还要能停下来 , 那才是真本事
virtual void stop() = 0;
//按喇叭会出声音 , 是滴滴叫 , 还是哔哔叫
virtual void alarm() = 0;
//引擎会轰隆隆地响 , 不响那是假的
virtual void engineBoom() = 0;
public:
void setSequence(vector<string> _sequence)
{
sequeue = _sequence;
}
//那模型应该会跑吧 , 别管是人推 , 还是电力驱动 , 总之要会跑
void run()
{
int len = sequeue.size();
for(int i = 0 ;i < len ;++i)
{
if(sequeue[i] == "start")
{
start();
}
else if(sequeue[i] == "stop")
{
stop();
}
else if(sequeue[i] == "alarm")
{
alarm();
}
else if(sequeue[i] == "engineBoom")
{
engineBoom();
}
}
}
private:
vector<string> sequeue;
};
//产品类 , 奔驰产品
class BenzModel : public CarModel
{
//独立业务处理
public:
void start() { cout<<"奔驰车跑起来是这个样子的..."<<endl; }
void stop() { cout<<"奔驰车应该这样停车..."<<endl; }
void alarm() { cout<<"奔驰车的喇叭声音是这个样子的..."<<endl; }
void engineBoom() { cout<<"奔驰车的引擎声音是这样的..."<<endl; }
};
//产品类 , 宝马产品
class BMWModel : public CarModel
{
//独立业务处理
public:
void start() { cout<<"宝马车跑起来是这个样子的..."<<endl; }
void stop() { cout<<"宝马车应该这样停车..."<<endl; }
void alarm() { cout<<"宝马车的喇叭声音是这个样子的..."<<endl; }
void engineBoom() { cout<<"宝马车的引擎声音是这样的..."<<endl; }
};
//抽象建造者
class CarBuilder
{
//设置产品的不同部分 , 以获得不同的产品
//建造产品
public:
virtual void setSequence(vector<string> sequence) = 0;
virtual CarModel* getCarModel() = 0;
};
//具体建造者 , 奔驰车建造者
class BenzBuilder : public CarBuilder
{
//设置产品的零件
//产品类内的逻辑处理
//组建一个产品
public:
BenzBuilder()
{
benz = new BenzModel();
}
CarModel* getCarModel()
{
return benz;
}
void setSequence(vector<string> _sequence)
{
benz->setSequence(_sequence);
}
~BenzBuilder()
{
delete benz;
}
private:
BenzModel* benz;
};
//具体建造者 , 宝马车建造者
class BMWBuilder : public CarBuilder
{
//设置产品的零件
//产品类内的逻辑处理
//组建一个产品
public:
BMWBuilder()
{
bmw = new BMWModel();
}
CarModel* getCarModel()
{
return bmw;
}
void setSequence(vector<string> _sequence)
{
bmw->setSequence(_sequence);
}
~BMWBuilder()
{
delete bmw;
}
private:
BMWModel* bmw;
};
//导演类
class Director
{
//构建不同的产品
//设置不同的零件 , 生产不同的产品
public:
Director(BenzBuilder* benz , BMWBuilder* bmw)
{
benzBuilder = benz;
bmwBuilder = bmw;
}
/*
*A型奔驰 , 先发动引擎 , 然后启动 , 然后停止 , 没有喇叭
*/
void getABenzModel()
{
//清空容器 , 防止意想不到情况
sequence.clear();
sequence.push_back("engineBoom");
sequence.push_back("start");
sequence.push_back("stop");
benzBuilder->setSequence(sequence);
//得到benz* , 调用run()方法
benzBuilder->getCarModel()->run();
}
/*
*B型奔驰 , 先按下喇叭 , 然后发动引擎 , 然后启动 , 最后停止
*/
void getBBenzModel()
{
//清空容器 , 防止意想不到情况
sequence.clear();
sequence.push_back("alarm");
sequence.push_back("engineBoom");
sequence.push_back("start");
sequence.push_back("stop");
benzBuilder->setSequence(sequence);
//得到benz* , 调用run()方法
benzBuilder->getCarModel()->run();
}
/*
*A型宝马 , 先按下喇叭 , 然后发动引擎 , 然后启动 , 最后停止
*/
void getABMWModel()
{
//清空容器 , 防止意想不到情况
sequence.clear();
sequence.push_back("alarm");
sequence.push_back("engineBoom");
sequence.push_back("start");
sequence.push_back("stop");
bmwBuilder->setSequence(sequence);
//得到benz* , 调用run()方法
bmwBuilder->getCarModel()->run();
}
/*
*B型宝马 , 先启动 , 最后停止
*/
void getBBMWModel()
{
//清空容器 , 防止意想不到情况
sequence.clear();
sequence.push_back("start");
sequence.push_back("stop");
bmwBuilder->setSequence(sequence);
//得到benz* , 调用run()方法
bmwBuilder->getCarModel()->run();
}
private:
vector<string> sequence;
BenzBuilder* benzBuilder;
BMWBuilder* bmwBuilder;
};
int main()
{
BenzBuilder* benz = new BenzBuilder();
BMWBuilder* bmw = new BMWBuilder();
Director director(benz,bmw);
director.getABenzModel();
cout<<"============================="<<endl;
director.getBBenzModel();
cout<<"============================="<<endl;
director.getABMWModel();
cout<<"============================="<<endl;
director.getBBMWModel();
delete benz;
delete bmw;
return 0;
}
#endif
运行结果 :
总结
大家也许会犯嘀咕,这个建造者模式和工厂模式非常相似,是的,非常相似,但是记住一点你就可以游刃有余地使用了:建造者模式最主要的功能是基本方法的调用顺序安排,也就是这些基本方法已经实现了,通俗地说就是零件的装配,顺序不同产生的对象也不同 ; 而工厂方法则重点是创建,创建零件是它的主要职责,组装顺序则不是它关心的 .
参考书籍 :
<<设计模式之禅 第二版>>
<<设计模式>>