简单工厂模式的遗留问题
//从上面的代码可以看到,简单工厂模式确实实现了new 出来具体对象, 和 业务逻辑的分离,
//但是不符合 "开闭原则"
//"开闭原则"说的是代码扩展性问题——对扩展开放,对修改关闭(封闭);
//假设过了两天,策划找到我们说:加一种怪物,新怪物类型:M_Beast(野兽类)
//那我们要怎么改呢?首先肯定是加一个 M_Beast类了,继承Monster
//然后MonsterFactory 中改动 createMonster方法完成。
//很显然,我们要改动到原先的 createMonster 方法,这是违反了 "开闭原则的"。
//那么如何改动才合理呢?这就要用到 "工厂方法" 模式
解决方案:工厂方法 模式
工厂方法(Factory Method)模式:简称工厂模式或者多态工厂模式。
//与简单工厂模式比,灵活性更强,实现也更加复杂,引入更多的新类。
//M_UndeadFactory,M_ElementFactory,M_MechanicFactory类,有一个共同的父类M_ParFactory(工厂抽象类):
//符合开闭原则,付出的代价是需要新增加多个新的工厂类。
//定义(实现意图):定义一个用于创建对象的接口(M_ParFactory类中的createMonster成员函数,这其实就是个工厂方法,工厂方法模式的名字也是由此而来),
//但由子类(M_UndeadFactory、M_ElementFactory、M_MechanicFactory)决定要实例化的类是哪一个。
//该模式使得某个类(M_Undead、M_Element、M_Mechanic)的实例化延迟到子类(M_UndeadFactory、M_ElementFactory、M_MechanicFactory)。
//出现新怪物类型:M_Beast(野兽类)。
//一般可以认为,将简单工厂模式的代码经过把工厂类进行抽象改造成符合开闭原则后的代码,就变成了工厂方法模式的代码。
代码实现
#include <iostream>
using namespace std;
//(1)简单工厂(Simple Factory)模式
//策划:亡灵类怪物,元素类怪物,机械类怪物:都有生命值,魔法值,攻击力三个属性。
//Monster作为父类,M_Undead(亡灵类),M_Element(元素类怪物),M_Mechanic(机械类怪物)。
namespace _namespace1 {
class Monster {
public:
Monster(int life, int magic, int attack) : m_life(life), m_magic(magic), m_attack(attack)
{
};
virtual ~Monster() {};
protected:
int m_life;
int m_magic;
int m_attack;
};
//M_Undead(亡灵类)
class M_Undead :public Monster {
public:
M_Undead(int life, int magic, int attack) :Monster(life, magic, attack) {
cout << "创建了一个亡灵类 life = " << m_life << " magic = " << m_magic << " attack = " << m_attack << endl;
}
};
//M_Element(元素类怪物)
class M_Element :public Monster {
public:
M_Element(int life, int magic, int attack) :Monster(life, magic, attack) {
cout << "创建了一个元素类怪物 life = " << m_life << " magic = " << m_magic << " attack = " << m_attack << endl;
}
};
//M_Mechanic(机械类怪物)
class M_Mechanic :public Monster {
public:
M_Mechanic(int life, int magic, int attack) :Monster(life, magic, attack) {
cout << "创建了一个机械类怪物 life = " << m_life << " magic = " << m_magic << " attack = " << m_attack << endl;
}
};
//所有工厂类的父类
class M_ParFactory
{
public:
virtual Monster* createMonster() = 0; //具体的实现在子类中进行
virtual ~M_ParFactory() {} //做父类时析构函数应该为虚函数
};
//每一个具体的类都有自己的工厂
class Undead_Factory :public M_ParFactory {
public:
Monster* createMonster() {
return new M_Undead(111, 222, 333);
}
};
class Element_Factory :public M_ParFactory {
public:
Monster* createMonster() {
return new M_Element(444, 555, 666);
}
};
class Mechanic_Factory :public M_ParFactory {
public:
Monster* createMonster() {
return new M_Mechanic(777, 888, 999);
}
};
//这里要有一个方法能够返回需要的具体的Monster,参数为M_ParFactory类型指针
Monster * g_createMonster_use_factory_method(M_ParFactory * parfactory) {
return parfactory->createMonster();
}
};
void testfactorymethod() {
_namespace1::M_ParFactory *par1 = new _namespace1::Undead_Factory();
_namespace1::Monster *pmon1 = _namespace1::g_createMonster_use_factory_method(par1);
_namespace1::M_ParFactory *par2 = new _namespace1::Element_Factory();
_namespace1::Monster *pmon2 = _namespace1::g_createMonster_use_factory_method(par2);
_namespace1::M_ParFactory *par3 = new _namespace1::Mechanic_Factory();
_namespace1::Monster *pmon3 = _namespace1::g_createMonster_use_factory_method(par3);
delete par1;
delete par2;
delete par3;
delete pmon1;
delete pmon2;
delete pmon3;
}
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);//程序退出时检测内存泄漏并显示到“输出”窗口
//"工厂方法" 模式
testfactorymethod();
std::cout << "Hello World!\n";
}
简单工厂模式 和 工厂方法模式 对比
//简单工厂模式把创建对象这件事放到了一个统一的地方来处理,弹性比较差。而工厂方法模式相当于建立了一个程序实现框架,从而让子类来决定对象如何创建。
//工厂方法模式往往需要创建一个与产品等级结构(层次)相同的工厂等级结构,这也增加了新类的层次结构和数目。
使用模版
//全局的用于创建怪物对象的函数,注意形参的类型是工厂父类类型的指针,返回类型是怪物父类类型的指针
Monster* Gbl_CreateMonster(M_ParFactory* factory,int canshu1,int canshu2,int canshu3)
{
return factory->createMonster1(canshu1, canshu2, canshu3); //createMonster虚函数扮演了多态new的行为,factory指向的具体怪物工厂类不同,创建的怪物对象也不同。
}
//-------------------
//不想创建太多工厂类,又想封装变化
//创建怪物工厂子类模板
template <typename T>
class M_ChildFactory :public M_ParFactory
{
public:
virtual Monster* createMonster1(int canshu1, int canshu2, int canshu3)
{
return new T(canshu1, canshu2, canshu3); //如果需要不同的值则可以通过createMonster的形参将值传递进来
}
};