设计模式-生成器模式

前两个文章我介绍了工厂方法模式和抽象工厂模式,这次我来讲一些生成器模式。生成器模式我也用的比较多。5个创建型模式里面,我比较喜欢用工厂方法模式,生成器模式和单例模式。

 

意图

将一个复杂对象的构建与它的表示分开,使得同样的构建过程可以创建不同的表示。

 

结构图

 

 

一眼看去是不是和抽象工厂模式有点像?是啊,我也觉得很像,有什么分别呢?别急,让我先讲一下builder 模式是怎么实现的。

为了更好的来解释生成器模式,我这里还画了个序列图,这个序列图清楚地描述了客户端是如何来使用生成器的。

 

 

我们还是以之前讲过的跑酷游戏的地形为例。先给出所有相关类的图:

产品类,没有变化,直接给出代码:

class CComponent
{
public:
	virtual void LoadPicture() = 0;
};

class CTerrain
{
public:

	virtual void SetBackground(CComponent* bg)
	{
		if (m_BackGround)
		{
			delete m_BackGround;
		}
		m_BackGround = bg;
	}
	virtual void SetGround(CComponent* ground)
	{
		if (m_Ground)
		{
			delete m_Ground;
		}
		m_Ground = ground;
	}


	CTerrain()
	{
		m_BackGround = NULL;
		m_Ground = NULL;
	}
	virtual ~CTerrain()
	{
		if (m_BackGround)
		{
			delete m_BackGround;
			m_BackGround = NULL;
		}

		if (m_Ground)
		{
			delete m_Ground;
			m_Ground = NULL;
		}
	}
protected:
	CComponent* m_BackGround;
	CComponent* m_Ground;

};



class CSnowBackground: public CComponent
{
public:
	virtual void LoadPicture()
	{
		std::cout<< "Load snow background picture \n"; 
	}
};

class CSnowGround: public CComponent
{
public:
	virtual void LoadPicture()
	{
		std::cout<< "Load snow ground picture\n"; 
	}
};

 

然后是关键的Builder类,首先我们抽象一个CBuilder类:

class CBuilder
{
public:
	virtual void MakeBackground() = 0;
	virtual void MakeGround() = 0;
	virtual CTerrain* GetTerrain() = 0;
};

 

里面有3个接口:

1. 创建一个背景实例,构建CTerrain对象的背景;

2. 创建一个地面实例,构建CTerrain对象的地面;

3. 返回一个CTerrain实例(Terrain由背景和地面组成)

注意这3个函数里面,只有第三个函数才返回一个CTerrain实例,另外2个函数都不返回任何东西。

接下来让我们看看CBuilder的一个具体实现子类:

class CSnowTerrainBuilder: public CBuilder
{
public:
	virtual void MakeBackground()
	{
		CComponent* bk = new CSnowBackground();
		bk->LoadPicture();
		_Terrain->SetBackground(bk);
	}
	virtual void MakeGround()
	{
		CComponent* ground = new CSnowGround();
		ground->LoadPicture();
		_Terrain->SetGround(ground);
	}
	virtual CTerrain* GetTerrain()
	{
		return _Terrain;
	}

	CSnowTerrainBuilder()
	{
		_Terrain = new CTerrain();
	}
protected:
	CTerrain* _Terrain;
};

从代码里面可以看到,我们在CSnowTerrainBuilder里面增加了一个CTerrain*的数据成员。也就是说CSnowTerrainBuilder聚合了一个CTerrain对象。

MakeBackground()和MakeGround()生成了2个CTerrain的组件,并且把生成的组件放到CTerrain对象里面(也就是构建CTerrain对象)。

下面我们来看一下CCreator类(也就是Builder模式里面的Director)

class CCreator
{
public:
	virtual CTerrain* Create(CBuilder& builder)
	{
		builder.MakeBackground();
		builder.MakeGround();

		return builder.GetTerrain();
	}
};


这个函数跟抽象工厂里面的那个函数很像,但是我们可以发现,抽象工厂CCreator::Create()里面的其中4行代码在生成器模式里面并没有:

class CCreator
{
public:
	void Create(CFactory& factory, CTerrain** t, CWeather** w)
	{
		CTerrain* terrain = factory.MakeTerrain();
		CComponent* bg = factory.MakeBackground();
		CComponent* ground = factory.MakeGround();
                
                //下面这4行代码并没有出现在Builder模式的版本里面,哪里去了呢?
                bg->LoadPicture();
		ground->LoadPicture();
		terrain->SetBackground(bg);
		terrain->SetGround(ground);

		CWeather* weather = factory.MakeWeather();

		*t = terrain;
		*w = weather;
	}
};

哈哈,看一下CSnowTerrainBuilder的代码就明白了,原来这些代码跑到CSnowTerrainBuilder里面去了。也也就是生成器模式的一个特点,把对象的构建给分离出去了(这些构建代码跑到Builder类里面去了)。

然后再看一下生成器模式的CCreator::Create(),我们会发现CCreator并不知道CTerrain的内部表示(MakeBackground和MakeGround并没有返回任何东西)。从接口名字也许可以知道Builder在创建背景和地面实例,但是并不知道具体在创建什么实例。从类图的角度讲生成器模式的Director(CCreator)类并不依赖于背景类和地面类。而抽象工厂模式却是依赖的。用G4的说法就是:生成器隐藏了对象CTerrain的内部表示。这就使得改变CTerrain的内部表示要容易一些,因为所有CBuilder的客户都不需要被改变。

 

最后看看客户端是怎么调用的:

CSnowTerrainBuilder builder;
	CCreator creator;
	creator.Create(builder);

	CTerrain* snowTerrain = builder.GetTerrain();//snowTerrain 就是builder创建出来的地形实例
	//这里可以用snowTerrain来做一些其他的事情

	delete snowTerrain;

相当的easy。

假如要build一个新的地形,比如森林地形,很简单,增加一个CBuilder的子类:CForestTerrainBuilder,然后在客户端里面创建一个CForestTerrainBuilder的对象,传给CCreator就ok了。

好了,生成器模式基本介绍完毕。文章的开始我们就讲了生成器模式和抽象工厂模式很像。那么到底有什么分别呢?

我们可以总计一下:

1. 抽象工厂模式的工厂类提供了一系列创建对象的接口,这些接口里面仅仅创建对象,而没有任何构建的动作。而生成器模式的builder类里面提供的接口实现了对象的构建。

2. 抽象工厂模式的工厂类的每一个接口都返回一个对象。而生成器模式的builder类的接口里面,只有一个函数返回最后构建完毕的对象,其他所有的接口并不返回任何东西。

3. 生成器模式着重于一步一步构造一个复杂对象。而抽象工厂模式着重于多个系列的产品对象(简单或者复杂的)。

4. 生成器模式在最后一步才返回产品,而抽象工厂模式是立即返回。

 

我个人的习惯就是当一个对象比较复杂的时候,比如这个对象有很多其他对象组成,然后这些组成相对比较容易会变动时,用Builder模式比较合适。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值