设计模式-工厂方法模式

小弟我最近重新学习设计模式。

经典就是经典,这本书我已经看了至少两遍了,这次重新再看一次,发现又体会到不同的东西了。

身边很多朋友都在写博客,那么我也凑凑热闹,一来可以加深自己的学习,二来也可以给朋友们参考参考。

限于本人能力有限,如果写的不好的地方请多多包涵,如果有什么理解错误的地方,欢迎指出。

好了,废话少说,切入正题。先从创建型模式开始吧。

我最熟悉的语言是C++,所以所有的例子都由C++来实现。

根据G4的说法,创建型模式包括: 工厂模式,抽象工厂模式,生成器模式,原型模式, 单件模式。先从工厂方法模式开始。


工厂方法模式(Factory Method)

意图:

定义一个用于创建对象的接口,让子类来决定实例化哪一个类。 Factory Method使一个类的实例化延迟到其子类。

结构图:

 

工厂模式是本人使用比较多的一种模式。根据G4的说法,我们可以首先考虑工厂模式,如果工厂模式不能满足需求,再考虑其他模式。
这个模式本身比较简单,主要分两部分:
1. 工厂方法类 (Creator)
2. 产品类 (Product)

我觉得这个模式的关键在于“使一个类的实例化延迟到其子类”。也就是说很多时候可能一个类并不知道它所需要创建的对象的类。

看上面的结构图,在class Creator里面有两种方法:

1. FactoryMethod

2. AnOperation

FactoryMethod()就是我们工厂方法模式提到的“工厂方法”, AnOperation是另外一个方法,这个方法可以是虚函数,也可以是非虚函数,个人比较喜欢虚函数,因为比较灵活。AnOperation需要用到FactoryMethod()创建出来的实例。而这些FactoryMethod()应该由Creator的子类(ConcreteCreator)来实现,Creator只是提供一个接口说明。也就是说Creator并不知道FacotryMethod()会创建什么类(ConcreteCreator知道)。我觉得这个是工厂方法模式的要点,其实也就是G4讲的“意图”。

讲设计模式最好的方式就是结合例子,我这里也来讲一个我自己用过的一个例子。
小弟我以前写过一个小游戏,一个跑酷游戏。这个游戏里面有个地形模块,地形模块有雪地地形,森林地形等。

那么如何来创建这些地形呢,我就用了个工厂方法模式。

我这里抽象了几个类

1. CTerrain,这是个表示地形的类,地形由2部分组成:背景和地面。

2. CComponent,这是个基类,也就是工厂方法模式中的“产品类”,

3. CCreator,就是用来创建地形(CTerrain)的一个类。

 

先来看看类图:

1. CCreator 是一个包括工厂方法的类

   CCreator里面有个方法叫做Create(), Create() 返回一个地形类(CTerrain),MakeBackground和MakeGround是虚函数,也就是工厂方法模式中的工厂方法(大多数情况下,这2个方法有子类来实现,当然基类也可以提供一个缺省实现,但是我觉得没什么必要)。函数Create()会调用MakeBackground和MakeGround来创建一个地形实例。

   CSnowCreator,CForestCreator是CCreator的子类,这两个类实现了工厂方法MakeBackground和MakeGround。

2. CComponent就是产品类

   我们可以看到它有几个子类。

那么我是怎么实现这几个类的呢,下面给出代码:

首先是产品类的基类,CComponent

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

很简单,里面就一个纯虚函数LoadPicture().

然后是它的四个子类

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"; 
	}
};


这4个类也很简单,就是实现了虚函数LoadPicture(),各自load自己的图片。

再一个类就是CTerrain, 这个类由2部分组成,一个背景类实例,一个地面类实例。

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;

};


好了,接下来我们看工厂方法模式的关键,CCreator类,先看下面的代码

class CCreator
{
public:
	CTerrain* Create()
	{
		CTerrain* terrain = MakeTerrain();
		CComponent* bg = MakeBackground();
		CComponent* ground = MakeGround();


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

		return terrain;
	}

protected:
	virtual CTerrain* MakeTerrain()
	{
		return new CTerrain();
	}
	virtual CComponent* MakeBackground() = 0;
	virtual CComponent* MakeGround() = 0;
};


从代码里面可以看到,这里有3个虚函数,这些就是工厂方法模式的工厂方法。

首先, MakeTerrain返回一个CTerrain实例。有些人觉得这个可以是虚函数,也可以是非虚函数。他们的区别也就是MakeTerrain()是否需要被子类继承来创建不同的Terrain,比如CTerrain的子类,我个人觉得这个没有绝对的。应该视具体情况而言。不过我的习惯是把它搞成虚的。

另外还有两个虚函数MakeBackground()和MakeGround(). 这2个函数一定是虚的(一般都是纯虚函数),因为需要子类来实现。(对于MakeTerrain,我也见过有些人不需要这个函数,而直接在Create()里面写死,返回一个Terrain实例,比如http://www.codeproject.com/Articles/35789/Understanding-Factory-Method-and-Abstract-Factory, 我觉得这个没什么关系,看个人需要了)

最后一个是public函数Create(),这个函数会调用MakeTerrain,MakeBackground,MakeGround来创建一个CTerrain实例,并且返回。也就是说这个函数并不知道MakeBackground和MakeGround会创建什么类型的产品。因为这些产品是在CCreator的子类里面才实现的。

下面给出CCreator两个子类的代码

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

class CForestCreator: public CCreator
{
public:
	virtual CComponent* MakeBackground()
	{
		return new CForestBackground();
	}
	virtual CComponent* MakeGround()
	{
		return new CForestGround();
	}
};


客户端怎么调用呢,看:

        //创建一个雪地地形
	CSnowCreator snowCreator;
	CTerrain* snowTerrain = snowCreator.Create();//这里就得到了一个CTerrain的实例,这是个雪地地形
	delete snowTerrain;
	

	//创建一个森林地形
	CForestCreator forestCreator;
	CTerrain* forestTerrain = forestCreator.Create();//得到森林地形
	
	delete forestTerrain;


 

好了,这个就是工厂方法模式的一个概要了。那么我们搞了半天,究竟用这个模式给我们带来什么好处呢。我觉得主要优点是:

客户端无需和具体的产品打交道,比方说客户端根本无需知道CSnowBackground类。也就是说我们通过工厂方法模式使得客户端和具体的产品类解耦。这个符合设计模式的一个基本要求:高内聚低耦合。客户端需要打交道的是创建产品的类CCreator,比如说我们现在需要增加一个新的地形沙漠,那么我们增加一个新的CCreator子类CDesertCreator和新的产品类CDesertBackground,CDesertGround。然后客户端创建一个CDesertCreator实例就行了,无需知道CDesertBackground和CDesertGround的存在。这样就比较容易扩展。

那么他有什么缺点呢:

每增加一个新的产品,可能就得增加一个CCreator子类。对于这个问题,我们可以用template来实现一个CCreator类,比如:

template<class BACKGROUND, class GROUND>
class CSuperCreator: public CCreator
{
public:
	virtual CComponent* MakeBackground()
	{
		return new BACKGROUND();
	}
	virtual CComponent* MakeGround()
	{
		return new GROUND();
	}
};


怎么调用呢,看下面:

        //使用模板类
	CSuperCreator<CSnowBackground, CSnowGround> creator;
	CTerrain* snowTerrain = creator.Create();
	delete snowTerrain;


如果要使用沙漠地形呢,可以这么搞

        //使用模板类
	CSuperCreator<CDesertBackground, CDesertGround> creator;
	CTerrain* desertTerrain = creator.Create();
	delete snowTerrain;


这样好像避免了增加新的CCreator子类,因为用template类来代替了。其实我个人是不太用template的,当然这个是个人喜好,怎么都行。

设计模式只是一些牛人总结出来的一些经验,没有绝对的,你觉得用的上就用,用不上也没必要死搬硬套,这样反而把事情搞复杂化。如果我们能从某个设计模式里面得到一些好处,那么就用。工厂方法模式还是挺实用的,简单易懂。我自己的习惯是首先考虑工厂方法模式,如果工厂方法模式不能满足我的要求,再考虑其他的,比如抽象工厂模式。抽象工厂模式比较接近于工厂方法模式,我们下一次就讲讲抽象工厂模式。另外工厂方法模式有点像行为模式里面的模板模式(template pattern),模板模式是把算法的一些步骤放到基类实现,而一些关键的算法或者比较会变动的算法让子类来实现。从某种意义上讲,也是把一些方法延迟到子类来实现。

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值