C++工厂模式总结(简易版反射)

来源:互联网 发布:mac os 优化 编辑:程序博客网 时间:2024/06/05 15:14

工厂模式解决了很多问题,但每当需要增加一个新功能时候,需要修改工厂类,违反了开闭原则,具体为什么见: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:
    virtual string PlayVideo()=0;
}

class VCD: public VideoWiring  
{
public:
    string PlayVideo()
    {
        return "正在播放播放VCD";
    }
}

class DVD: public VideoWiring  
{
public:
    string PlayVideo()
    {
        return "正在播放播放DVD";
    }
}

1.简单工厂
class Create
{
public:
    static VideoWiring* factory(string  VideoName)
    {
        switch(VideoName)
        {
            case "DVD":
                return new DVD();
            case "VCD":
                return new VCD();
        }
        return null;
    }
}

client端代码:
void PlayVideo()
{
     VideoWiring *vw=Create.factory("DVD");
     vw->PlayVideo();
     delete vw;
     
     vw=Create.factory("VCD");
     vw->PlayVideo();
     delete vw;
}
好处是:
1、充分利用了多态性不管什么具体产品都返回抽象产品。

2、充分利用了封装性,内部产品发生变化时外部使用者不会受到影响。       

缺点是:如果增加了新的产品,就必须得修改工厂(Factory),不满足闭合原则。

2.工厂方法
class Create
{
public:
    virtual VideoWiring* factory()=0;
}

class DVDCreate: public Create
{
    VideoWiring* factory()
    {
        return new DVD();
    }
}

class VCDCreate: public Create
{
    VideoWiring* factory()
    {
        return new VCD();
    }
}
client端代码:
void PlayVideo()
{
     VideoWiring *dvd,*vcd;
     Create *dvdCreate,*vcdCreate;
    
     dvdCreate=new DVDCreate();
     dvd=dvdCreate->factory();
     dvd->PlayVideo();
     delete dvd;
    
     vcdCreate=new VCDCreate();
     vcd=vcdCreate->factory();
     vcd->PlayVideo();
     delete vcd;
}
工厂方法克服了简单工厂的缺点,增加新的产品时,不必修改现存的代码,而只需增加新代码。满足开闭原则。

方法一:

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.csdn.net/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 dynamiclystruct 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是比较好用的办法,可以用在不同的场合。


0 0