[C++实现 设计模式(5)] : 建造者模式

16 篇文章 0 订阅

情景分析

       还是让我们生产车模 , 奔驰和宝马, . 这回额外增加了一个新的需求 , 那就是汽车的启动 , 停止 , 喇叭声音 , 引擎声音都是由客户自己控制 , 他想什么顺序就什么顺序 . 那我们还是按照模板类的方式完成(这里可以直接理解成一个汽车的模型有上述四种功能即可, 不用翻代码) . 然后 , 我们来模拟一下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

运行结果 :
在这里插入图片描述

总结

       大家也许会犯嘀咕,这个建造者模式和工厂模式非常相似,是的,非常相似,但是记住一点你就可以游刃有余地使用了:建造者模式最主要的功能是基本方法的调用顺序安排,也就是这些基本方法已经实现了,通俗地说就是零件的装配,顺序不同产生的对象也不同 ; 而工厂方法则重点是创建,创建零件是它的主要职责,组装顺序则不是它关心的 .

参考书籍 :

                  <<设计模式之禅 第二版>>
                  <<设计模式>>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值