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

工厂模式解决了很多问题,但每当需要增加一个新功能时候,需要修改工厂类,违反了开闭原则,具体为什么见: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 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是比较好用的办法,可以用在不同的场合。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值