设计模式-抽象工厂模式

上次我讲了一下工厂方法模式,这次我来讲一下抽象工厂模式。

其实,我在工作当中基本没有用到过抽象工厂模式。我用的比较多的是工厂方法模式。我对抽象工厂模式的理解主要来自《设计模式》这本书。同时也搜索了一些网上的文章。

这里我就讲一下我自己的理解,如果讲的不好,或者根本就讲错了,请大家谅解。

 

抽象工厂模式 (Abstract Factory)

意图

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

 

结构图

从结构图来看,抽象工厂模式主要包括:工厂类和产品类。

我们可以从意图里面看到,抽象工厂模式的主要目的就是“提供一系列创建对象的接口”

我个人的理解是,抽象工厂模式是比工厂方法模式更加抽象的一种模式。当我们的程序里面有很多产品类时,我们就可以使用抽象工厂模式。

我们还是以上一篇文章里面的跑酷游戏(http://blog.csdn.net/zj510/article/details/8089191)的地形为例子。

之前我们用了工厂方法模式来创建地形,这次我们尝试用抽象工厂模式来创建。

我们这次增加一个新的产品类CWeather,这个类用来表示和地形有关系的天气效果。

先给出类图

从类图我们可以看到我们新增加了一个产品系列,天气类CWeather以及一个工厂类CFactory。

地形类以及地形类里面的背景,地面类和上一个文章一模一样,没有任何改动。

这里直接列出代码

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 CForestBackground: public CComponent
{
public:
	virtual void LoadPicture()
	{
		std::cout<< "Load forest background picture \n"; 
	}
};

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

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


接下来给出天气类CWeather,这个类里面只有一个函数Effect()

class CWeather
{
public:
	virtual void Effect() = 0;
};

class CSunWeather: public CWeather
{
public:
	virtual void Effect()
	{
		std::cout<< "Add sun effect \n";
	}
};

这里我们只用一个子类CSunWeather来作为示例。这个类显示一个太阳效果。

 

然后看关键的工厂类:

class CFactory
{
public:
	//生产一个Terrain对象
	virtual CTerrain* MakeTerrain()
	{
		return new CTerrain();
	}
	virtual CComponent* MakeBackground() = 0;
	virtual CComponent* MakeGround() = 0;

	//生产一个天气对象
	virtual CWeather* MakeWeather() = 0;
};

class CSnowSunFactory: public CFactory
{
public:
	virtual CComponent* MakeBackground()
	{
		return new CSnowBackground();
	}
	virtual CComponent* MakeGround()
	{
		return new CSnowGround();
	}

	virtual CWeather* MakeWeather()
	{
		return new CSunWeather();
	}

	static CSnowSunFactory& GetInst()
	{
		static CSnowSunFactory factory;
		return factory;
	}
private:
	CSnowSunFactory(){}
};


工厂类总共提供了4个接口,用来创建4个产品地形,背景,地面和天气。

其中地形类组合了背景和地面。

背景和地面是同一个层面的类。

天气是另外的一个类。

子工厂类CSnowSunFactory实现了这4个接口,然后生产雪地背景,雪地地面和太阳效果。由雪地背景和雪地地面来组成地形实例。

 

ok,现在再来看看CCreator类,这个类和上一个文章(工厂方法模式)里面的CCreator是一样的职责,这里我不过是改了成员函数Create的参数,主要就是用来传递一个factory实例进去,这样CCreator可以根据factory参数来创建相应的产品。看代码:

class CCreator
{
public:
	void Create(CFactory& factory, CTerrain** t, CWeather** w)
	{
		CTerrain* terrain = factory.MakeTerrain();
		CComponent* bg = factory.MakeBackground();
		CComponent* ground = factory.MakeGround();

		bg->LoadPicture();
		ground->LoadPicture();
		terrain->SetBackground(bg);
		terrain->SetGround(ground);

		CWeather* weather = factory.MakeWeather();

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


 ok, Create()函数返回2个对象,地形和天气。

其中地形部分和工厂方法模式中的一模一样,只是增加了天气对象。

(对于CTerrain对象,我们这里没有子类化,其实也是可以。)

客户端代码:

CSnowSunFactory& factory = CSnowSunFactory::GetInst();
	CCreator creator;
	CTerrain* terrain = NULL;
	CWeather* weather = NULL;

	creator.Create(factory, &terrain, &weather);

	weather->Effect();

	delete terrain;
	delete weather;

上面的代码例子创建了一个雪地地形配合下雪天气。

仔细的把抽象工厂模式和工厂方法模式相比,我们会发现,其实抽象工厂模式只是增加了一个工厂类CFactory,然后把CCreator里面的工厂方法移到了工厂类中。

然后通过传递一个参数,把CFactory的实例传到CCreator里面。

如果我们要增加森林配合下雨天气的场景,那么只需要增加一个新的工厂类,CForestRainFactory,然后实现相应代码(省略,实现跟CSnowSunFactory差不多)。

然后这样调用来创建森林,下雨效果。

CForestRainFactory& factory = CForestRainFactory::GetInst();
	CCreator creator;
	CTerrain* terrain = NULL;
	CWeather* weather = NULL;

	creator.Create(factory, &terrain, &weather);

	weather->Effect();

	delete terrain;
	delete weather;


通常我们把工厂类设计成单例的,因为一个工厂类通常只生产一个特定的产品系列。

 

那么抽象工厂模式有什么优点呢?

1. 首先,当我们的需求里面产品系列比较多或者复杂的时候,用一个专门的CFactory类来管理会比较好。这样实现了客户类和产品创建的分离;

2. 很容易更改一个工厂,从而更改所有的产品,因为一个工厂类实现了一个完整的产品体系;

3. 一个工厂类封装了一个完整的产品体系,那么也就是说一个应用一次只能使用同一个系列中的产品(由当前的工厂类决定)。

当然抽象工厂模式也有个很明显的缺点:

难以支持新的产品。 看上面的例子,CFactory里面只提供了4个接口。如果要支持新的产品,就得增加一个接口,这就会影响所有的子类。

这里有一个办法,通过参数来控制,比如我们增加一个接口:virtual void* MakeMagicProduct() = 0;

不同的工厂之类可以返回不同的产品。那么这里有个问题,对于调用者来说,怎么知道返回的是个什么类型呢?

比如:

void Create(CFactory& factory)
{
      void* p = factory.MakeMagicProduct();
}

那么怎么知道p是什么的?这个好像比较困难。如果用强行转换的话,感觉不太安全,或者不具有通用性。

我的理解是,真要这么做的话,就得小心一点。不然就直接改接口吧。

 

那么什么时候用工厂方法模式,什么时候用抽象工厂模式呢?这里引用书上对于什么时候使用抽象工厂模式的原话:

1. 一个系统要独立于它的产品的创建,组合和表示时;

2. 一个系统要由多个产品系列中的一个来配置时;

3. 当你要强调一系列相关的产品对象的设计以便进行联合使用时;

4. 当你提供一个产品类库,而只想显示他们的的接口而不是实现时。

上面的4点需要细细体会,其中第二点是最容易理解的,一个配置就是一个CFactory子类的实例。然后另外3点,我觉得可以简单理解为产品结构比较复杂并且有一定联系(比如地形是和天气联系在一起的)的时候。

 

回到跑酷例子,因为产品体系不复杂,而且我的那个小游戏里面,游戏场景是可以事先设定的,也就是说当玩家登录游戏主界面的时候,使用什么地形已经决定了。然后里面有一个CCreator的数据成员来专门创建各种地形,所以我的做法是每次登录主界面的时候就相应创建一个CCreator的子类的实例,然后赋值给CCreator数据成员。CCreator并不知道要创建什么背景,地面,这些是在CCreator的子类里面决定。所以这个地方用了工厂方法模式。

当然我们也可以用抽象工厂模式来实现。只是我觉得用工厂方法模式已经足够了。

这里再重复一次G4的一个建议:通常,设计以使用Factory Method开始,并且当设计者发现需要更大的灵活性时,设计便会向其他创建型模式演化。


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值