工厂方法模式
23种设计模式种创建型设计模式之《工厂方法模式》,前面的工厂方法模式引导中的例子最后的优缺点中的缺点是需要解决的,而工厂方法将是一种解决改善的办法,注意我说的是其中一种的一种的而不是唯一的一种。那么下面我们来看看GOF的《工厂方法模式》;
相关概念
意图:Factory Method( 工厂方法)模式的意思是定义了一个创建产品对象的接口(抽象类提供接口函数),将实际创建的工作推迟到子类中。即让子类决定决定实例化哪一个类,这样工厂方法模式使一个类的实例化延迟到了子类。就是对Factory 工厂类进一步抽象,让它产生一个基类它的职责就是接口函数没有实现它的功能就是创建让子类去继承它实际的产品创建都在子类当中进行之后利用虚函数多态调用基类的Create方法拿到实际所需要创建产品的指针。
1、核心工厂类不在负责产品的创建,这样核心类成为一个抽像工厂角色,仅负责具体工厂子类必须实现的接口(抽象接口类)。
2、这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品(这也是工厂方法引导里面所不能解决的)。
3、GOF的工厂模式其实就是上篇的工厂方法引导的延伸,解决了许多上篇的问题,有如下2个好处
3.1 首先完全实现‘开-闭’原则(面向对象7大原则之一:修改不行,扩展可以),实现了可扩展性。
3.2 其次更复杂的层次结构,可以应用于产品结果复杂的场合。(即一个产品可能有多个组成,再最下面有例子代码讲解)
别名:虚构建器(virtual Constructor)即虚函数多态绑定
// ConsoleApplication3.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
//int _tmain(int argc, _TCHAR* argv[])
//{
// return 0;
//}
//包含必要的头文件
#include <iostream>
#include <assert.h>
class FiscalDiskBase
{
public:
virtual ~FiscalDiskBase(){}
virtual void PrintName()
{
printf("FiscalDisk生产厂商:%s\n", m_strName.c_str());
}
protected:
//默认构造函数为保护访问级别
//意味着FiscalDiskBase 不能被实例化必须继承
FiscalDiskBase(){}//无参构造
FiscalDiskBase(const std::string & name) :m_strName(name){
}//有参构造
std::string m_strName;//FiscalDisk厂商名称
};
//生产同一种产品有多家
//爱信诺
class AisinoFiscalDisk :public FiscalDiskBase
{
public:
AisinoFiscalDisk() :FiscalDiskBase("爱信诺航天信息")
{
}
void PrintName()
{
printf("Aisin Text \n");
FiscalDiskBase::PrintName();
}
};
//宏思
class HSFiscalDisk :public FiscalDiskBase
{
public:
HSFiscalDisk() :FiscalDiskBase("百旺金穗")
{
}
void PrintName()
{
printf("HS Text \n");
FiscalDiskBase::PrintName();
}
};
//国家信息安全技术研究中心(National Information Security Engineering Center)
class NisecFiscalDisk :public FiscalDiskBase
{
public:
NisecFiscalDisk() :FiscalDiskBase("国家安全总局")
{
}
void PrintName()
{
printf("Nisec Text \n");
FiscalDiskBase::PrintName();
}
};
//抽象工厂提供接口
class IFactory{
public:
virtual FiscalDiskBase* Create() = 0; //返回基类的指针
};
//有多少产品就有多少个工厂 子类实现
class AisinoFactory :public IFactory{
public:
FiscalDiskBase* Create()
{
return new AisinoFiscalDisk();
}
};
class HSFactory :public IFactory{
public:
FiscalDiskBase* Create()
{
return new HSFiscalDisk();
}
};
class NiseFactory :public IFactory{
public:
FiscalDiskBase* Create()
{
return new NisecFiscalDisk();
}
};
//测试使用方法函数
void FactoryMethodTest()
{
//定义抽象类和产品基类的指针 虚函数实现多态
IFactory *pFactory = NULL; //抽象基类类
FiscalDiskBase *pDisk = NULL; //产品基类
//定义具体工厂实例 栈分配
AisinoFactory asFactroy;
HSFactory hsFactory;
NiseFactory nsFactory;
pFactory = &asFactroy;//抽象基类指向实际工厂。
pDisk = pFactory->Create();//虚函数多态实际调用了基类的Create 里面new了
pDisk->PrintName(); //虚函数多态掉了子类的,然后子类显示掉了基类的。
delete pDisk;//释放 防止内存泄漏
pFactory = &hsFactory;
pDisk = pFactory->Create();
pDisk->PrintName();
delete pDisk;
pFactory = &nsFactory;
pDisk = pFactory->Create();
pDisk->PrintName();
delete pDisk;
pDisk = NULL;
pFactory = NULL;
}
/**
* 主函数
*/
int main(void)
{
FactoryMethodTest();
getchar();
}
/*这种增加了一个接口创建类 解决了不需要修改工厂类 ,
而只需要增加新类继承只接口类来创建新的产品完美解决了
简单工厂模式的缺陷(是需要修改工厂类增加新的产品)
*/
UML图 :
可以UML图多出了 抽象接口类 ,多个子类工厂继承。创建了实际的子类产品,返回了基类指针。其实可以看出工厂方法模式也是非常简单的多出了一个抽象层有多少产品就必须实现多少对应的工厂子类用于创建这些产品。适用性:
1、当一个类不知道它所必须创建的对象的类的时候。
2、当一个类希望由它的子类来指定它所创建的对象的时候。
3、当类将创建的对象的职责委托给多个帮助子类中的某一个,并且你希望将那一个帮助子类是代理者这一信息局部化的时候。
主要应用于两种情况
1、对于某个产品,调用者清楚地知道应该使用那个具体工厂服务,实例化该具体工厂,生产出具体的产品来
2、只需要一种产品,而不想知道也不需要知道究竟是哪个工厂为生产的,即最终选用那个具体的工厂的决定权在生产者一方,他们根据当前系统的情况来实例化一个具体的工厂返回给使用者,而这个决策过程这对于使用者来说是透明的。
终结:使用了工厂方法虽然进行了解耦,使过程更加灵活,但是也带来了类膨胀的后果。
如果我们使用工厂方法仅仅是为了解决工厂方法引导中前面的提到缺陷(不用修改工厂类),那么会发现有点小题大做了。
C++的模板机制也能解决。这就是我最前面为什么说不是唯一的解决方法。C++模板可以解决非模版工厂方法引导的缺陷,因为模板中的类型化的替参数来进行相关的创建。
那么如何使用C++模版机制来解决工厂方法引导中提到的缺点呢?
那么就有2种方法进行解决
1、返回不确定性的任意一个类的实例这个版本返回T,T是FiscalDiskBase的实际子类。
代码如下
class STFiscalDiskFactory{ //替代引导中的FiscalDiskFactory
public:
template <typename T>
static T* Create()//函数模板
{
return new T();
}
};
2、确定性的基类这个版本返回FiscalDiskBase基类,这个方法模板参数T必须继承自FiscalDiskBase 的子类。
//ST:表示标准模板
class STFiscalDiskBaseFactory{
public:
template <typename T>
static FiscalDiskBase *Create()//替代引导中的FiscalDiskFactory
{
return new T();
}
};
以上2段代码进行测试的代码
//第一种
void STFactoryTest() //这个方法更加通用
{
FiscalDiskBase *pCard = NULL;
pCard = STFiscalDiskFactory::Create<AisinoFiscalDisk>();
pCard->PrintName();
delete pCard;
pCard = STFiscalDiskFactory::Create<HSFiscalDisk>();
pCard->PrintName();
delete pCard;
pCard = STFiscalDiskFactory::Create<NisecFiscalDisk>();
pCard->PrintName();
delete pCard;
pCard = NULL;
}
//第二种
void STFactoryBaseTest()//代码一样
{
FiscalDiskBase *pCard = NULL;
pCard = STFiscalDiskFactory::Create<AisinoFiscalDisk>();
pCard->PrintName();
delete pCard;
pCard = STFiscalDiskFactory::Create<HSFiscalDisk>();
pCard->PrintName();
delete pCard;
pCard = STFiscalDiskFactory::Create<NisecFiscalDisk>();
pCard->PrintName();
delete pCard;
pCard = NULL;
}
测试结果和上面一模一样。这里不再贴图了。只是在main函数换掉测试函数即可。
上面我们说了工厂方法有2个好处其中第二个好处就是容易实现更加复杂的构造。那么它是如何实现更加复杂的构造的呢?
比如我们要生产一个盘(FiscalDisk),该盘将由外壳、电路盘PCB、以及软件组合而成,在这种情况下我们呢就需要定义3个类。
注意我们需要在不修改前面源代码的基础上扩展,开闭原则。