工厂模式解决了很多问题,但每当需要增加一个新功能时候,需要修改工厂类,违反了开闭原则,具体为什么见:blog.sina.com.cn/s/blog_48ebca64010005of.html
文中解决工厂问题主要采用两种方法:
1、为每个功能创建一个Creator,该Creator继承自抽象Creator,用户可以使用抽象Creator创建各个功能的Creator,好处是保证了开闭原则,当新增功能时,不需要修改原来的工厂创建代码,只需要新增一个Creator,一个功能类,然后修改调用处;
若不采用此法则需要新增一个功能类,然后修改原来的工厂类,增加创建模块,违反开闭原则。
具体实现转自一位大侠的博客(blog.sina.com.cn/s/blog_48ebca64010005of.html):
首先定义产品类及其子类:
class VideoWiring
{
public:
}
class VCD: public VideoWiring
{
public:
}
class DVD: public VideoWiring
{
public:
}
1.简单工厂
class Create
{
public:
}
client端代码:
void PlayVideo()
{
}
好处是:
1、充分利用了多态性不管什么具体产品都返回抽象产品。
2、充分利用了封装性,内部产品发生变化时外部使用者不会受到影响。
缺点是:如果增加了新的产品,就必须得修改工厂(Factory),不满足闭合原则。
2.工厂方法
class Create
{
public:
}
class DVDCreate: public Create
{
}
class VCDCreate: public Create
{
}
client端代码:
void PlayVideo()
{
}
工厂方法克服了简单工厂的缺点,增加新的产品时,不必修改现存的代码,而只需增加新代码。满足开闭原则。
方法一:
class VideoWiring
{
public:
virtual string PlayVideo()=0;
};
class VCD: public VideoWiring
{
public:
string PlayVideo()
{
return "正在播放播放VCD";
}
static VideoWiring* factory()//-------------------------注意此行
{
return new VCD();
}
};
class DVD: public VideoWiring
{
public:
string PlayVideo()
{
return "正在播放播放DVD";
}
static VideoWiring* factory()//-----------------------------注意此行
{
return new DVD();
}
};
class SVCD: public VideoWiring
{
public:
string PlayVideo()
{
return "正在播放播放SVCD";
}
static VideoWiring* factory()//--------------------------------注意此行
{
return new SVCD();
}
};
typedef void* (*Callback)();
const Callback ptrs[] =
{
(Callback)VCD::factory,
(Callback)DVD::factory,
(Callback)SVCD::factory</span>
};
//下面是调用逻辑
VideoWiring* items[16];
sz= sizeof(ptrs) / sizeof(ptrs[0]);
for (int i = 0; i < sz; i++)
{
items[i] = (VideoWiring*)(ptrs[i]());
printf("%s\r\n", items[i]->PlayVideo().c_str());
}
若是新加一个功能,只需要新加一个类,然后实现factory方法,然后在调用处新增一个数组项就可以了,是不是OK了?
上面有个问题就是有太多的factory方法是类似的,怎么解决呢,我们可以使用template,定义个Creator基类
template<class T>
class bs
{
public:
static void * Create()
{
return new T;
}
};
在定义个功能类的基类:
class ko
{
public:
virtual void PlayVideo()=0;
};
然后功能类都继承与此类
class bs1:public bs<bs1>,public ko
{
public:
bs1()
{
a = "bs111";
}
void it(){}
void PlayVideo()
{
printf("%s\r\n", a.c_str());
}
private:
string a;
};
class bs2:public bs<bs2>,public ko
{
public:
bs2()
{
b = "bs222";
}
void PlayVideo()
{
printf("%s\r\n", b.c_str());
}
private:
string b;
};
然后定义指针数组
const Callback ptrs[] =
{
(Callback)bs1::Create,
(Callback)bs2::Create
};
然后实例化
ko *k[16];
int sz= sizeof(ptrs) / sizeof(ptrs[0]);
for (int i = 0; i < sz; i++)
{
k[i] = (ko*)ptrs[i]();
k[i]->PlayVideo();
}
好像成功了,咋看是这样的,其实反而把bs1,bs2类引入到了客户端,暴露了细节类,还是得此失彼,此法宣告失败!终究还是没有绕开后续添加新类需要修改创建器的问题。
2、采用反射技术,当然只是简单反射。好处是新增功能类,只需要在功能类中实现反射,然后修改调用出,保证了开闭原则。
此处有2种方法可以实现(实际只有一种)
{1}[不推荐使用,太麻烦]一种是使用一个基类,在基类中折腾,借助了一些中间类,嵌入反射逻辑:此法用了很多模板定义,后续的类必须继承此类才能实现反射,具体实现参见:
http://blog.youkuaiyun.com/nighsen/article/details/6407017
具体实现如下:
定义个模板基类,所以类必须继承自该类
template<class T, char name[]>
class RegisterItem
{
public:
RegisterItem()
{
}
~RegisterItem()
{
}
static void* CreateInstance()
{
return new T;
}
public:
static RegistyInfo rc; //放到子类里面去初始化,否则会有问题
};
//定义模板类中使用的静态成员结构
typedef void* (*CreateFuntion)(void);
class ClassFactory
{
public:
static void* GetClassByName(std::string name);
static void RegistClass(std::string name,CreateFuntion method);
static std::map<std::string, CreateFuntion>& getMap();
};
struct RegistyInfo
{
RegistyInfo(std::string name, CreateFuntion method)
{
ClassFactory::RegistClass(name, method);
}
};
</pre><pre name="code" class="cpp">//下面是实现:
void* ClassFactory::GetClassByName( std::string name )
{
std::map<std::string,CreateFuntion>::const_iterator find;
find = ClassFactory::getMap().find(name);
if(find==ClassFactory::getMap().end())
{
return NULL;
}
else
{
return find->second();
}
}
void ClassFactory::RegistClass( std::string name,CreateFuntion method )
{
ClassFactory::getMap().insert(std::make_pair(name,method));
}
std::map<std::string, CreateFuntion>& ClassFactory::getMap()
{
static std::map<std::string, CreateFuntion> pMap;
return pMap;
}
//下面是功能基类定义
class BaseItem
{
public:
virtual void Play() = 0;
};
//下面是实际功能类定义
extern char p[];
class GMText:public RegisterItem<GMText, p>,public BaseItem
{
public:
GMText(void);
~GMText(void);
void Play();
};
//下面是实现
extern char p[]="GMText";
RegistyInfo RegisterItem<GMText,p>::rc(p, RegisterItem<GMText, p>::CreateInstance);//看到了吧,在这边实例化的,一份子类里面就有独立的一份静态成员拷贝,每一份都需要自己初始化
GMText::GMText(void)
{
RegisterItem::rc;
}
GMText::~GMText(void)
{
}
void GMText::Play()
{
printf("GMText.play\r\n");
}
// 下面是调用逻辑
BaseItem* tmp=(BaseItem*)ClassFactory::GetClassByName("GMText");
tmp->Play();
测试OK。经过此次试验我们可以发现一下问题:
(1)类模板的定义和实现必须在同一文件中,无法分离,若类模板中定义了静态变量,则需要在主程序开始处进行初始化,否则因为模板类会被多个CPP包含,就出现静态变量被多次初始化的情况,会出现xxx在xxx.obj中已经存在的问题。
(2)类模板中的基础类型定义有讲究,class <T, char name[]>中的char[]name是一种定义好的类型,使用时必须如下:
模板定义
template<class T, char name[]>
class test
//使用模板
const char q[]="demo";
test<CDemoClass, q> testObject;
只能使用int,long,int*,char []等类型,float,double不能使用,特别的char[]需要在外部定义个char a[]="test";样色的全局字符串才可以传入。
{2}避开模板,使用宏定义在每个功能类中加入反射逻辑,推荐此法,参见http://www.cnblogs.com/jiezhi/archive/2006/07/12/448962.html数据定义:
</pre><p>cpp">class DynBase;
struct ReflectInfo;
bool Register(ReflectInfo* ci);
typedef void* (*funCreateObject)();
//Assistant class to create object dynamicly
struct ReflectInfo
{
public:
std::string Type;
funCreateObject Fun;
ReflectInfo(std::string type, funCreateObject fun)
{
Type = type;
Fun = fun;
Register(this);
}
~ReflectInfo()
{
printf("fuck\r\n");
}
};
#define DEFINE_REFLECT(class_name)\
private:\
static ReflectInfo m_cInfo;\
public:\
static void* CreateClass##class_name(){return new class_name();};
#define DEFINE_REFLECT_IMP(class_name)\
ReflectInfo (##class_name::m_cInfo)(#class_name,(funCreateObject)(##class_name::CreateClass##class_name));
下面是使用接口类
DynBase.h
//The base class of dynamic created class.
//If you want to create a instance of a class ,you must let
//the class derive from the DynBase.
class DynBase
{
public:
static bool Register(ReflectInfo* classInfo);
static void* CreateObject(string type);
private:
static std::map<string,ReflectInfo*> m_classInfoMap;
};
DynBase.cpp
std::map< string,ReflectInfo*> DynBase::m_classInfoMap = std::map< string,ReflectInfo*>();
bool Register(ReflectInfo* ci)
{
return DynBase::Register(ci);
}
bool DynBase::Register(ReflectInfo* classInfo)
{
m_classInfoMap[classInfo->Type] = classInfo;
return true;
}
void* DynBase::CreateObject(string type)
{
if ( m_classInfoMap[type] != NULL )
{
return m_classInfoMap[type]->Fun();
}
return NULL;
}
下面是功能类示例
xxx.h文件
class GMOrange
{
public:
GMOrange(void);
~GMOrange(void);
DEFINE_REFLECT(GMOrange)//添加此行------------只要看这边就可以-------------------------
public:
void Init();
void UnInit();
};
xxx.cpp文件:
DEFINE_REFLECT_IMP(GMOrange)//添加此行------------只要看这边就可以-------------------------
GMOrange::GMOrange(void)
{
printf("orange construct\r\n");
}
GMOrange::~GMOrange(void)
{
printf("orange deconstruct\r\n");
}
void GMOrange::UnInit()
{
printf("destroy orange\r\n");
}
void GMOrange::Init()
{
printf("create orange\r\n");
}
下面是调用示例
GMApple* instance = (GMApple*)DynBase::CreateObject("GMApple");
instance->Init();
instance->UnInit();
delete instance;
此法经测试可用,问题是存在重复编码问题,不符合设计原则,但没办法,要自动化就得有些代价。
若有建议欢迎留言。
总结:
1、在每个功能创建器类中定义个factory方法,返回自己new的对象,然后定义一个指针数组,调用处遍历这个数组,调用数组中的函数指针,创建对象;这样只需要做:
新增一个功能类,新增一个功能创建器类实现factory方法,在调用处增加factory方法指针,OK
2、定义个只含有factory静态方法的template类,让每个功能类继承这个template类,这样每个功能类就都有一个自己的factory,而且函数代码是一样的,克服1中的问题;这样需要做:
新增一个功能类,新增一个功能创建器类继承template类,在调研处增加此类的factory指针,OK
3、定义一个含有factory静态方法的template类,定义个静态的数据成员(这个成员是个结构体,在结构体的构造函数中加入注册类逻辑),每个功能类继承这个template类,这样每个功能类就各有一个静态数据成员,功能类需要在自己的实现中初始化这个成员,这样才能注册功能类,在调用的地方使用注册类的静态方法获取功能类指针。这样需要做:
新增一个功能类,继承template类,在功能类实现处初始化基类静态成员,在调研处字串数组中加入这个字串(通过GetXXXbyName()直接获取功能类指针),OK
4、文中最后一个办法,定义一个专门反射的类,哪个类需要反射,则假如对于的宏进行注册,使用方面。
综上 我觉得3,4是比较好用的办法,可以用在不同的场合。