您的位置:首页 > 产品设计 > UI/UE

设计模式-生成器模式

2012-10-22 17:31 148 查看
前两个文章我介绍了工厂方法模式和抽象工厂模式,这次我来讲一些生成器模式。生成器模式我也用的比较多。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模式比较合适。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息