C++实现工厂模式

本文介绍了一种在C++中实现工厂模式的方法,该方法利用宏定义和静态注册机制,实现了一个能够根据类名创建对象的工厂。这种方法克服了C++缺乏反射机制的问题,提供了良好的扩展性和面向接口编程的支持。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        工厂模式,想必大家都很熟悉,工厂模式封装了产品的生产,用户无需知道对象是怎么生产出来的,只要给定一个产品名称,就可以得到相应的的产品。在Java和C#中,对用相应的反射机制可以利用,很好的实现了工厂模式。但是在C++没有反射机制,怎么实现呢?下面一一详解。

        在工厂中,用一个map是用来保存类名与生产者映射关系,在注册的时候将类名与对应关系设置好,生产对象和销毁对象都是以这个map为核心,用类名取得生产者,然后在用生产者获得产品。其中编程细节各位可以自己琢磨。

        在这个工厂模式中,有以下优点:

       1.客户端不需要知道产品怎么生产,只需要产品名称(类名),就可以得到一个对象。

       2.良好的拓展性,如果需要往工厂中增加生产者时,只需要将某个类注册进去即可,无需修改代码。

       3.面向接口编程的标志,定义接口后,工厂只对接口进行操作,没有关心实现,将实现与使用场景分离。

      当然也有不好的地方,生产者不够灵活,只能调用无形参构造来生产对象,因为需要抽象共性,并且编程语言的局限性,利用宏来定义类,使得有形参的生产者较难实现。

        工厂类中,包含了各种生产者,在语言层面,就是每个类都对应一个生产者,工厂实际是用类名索引到对应的生产者,然后调用生产者生产对象,这个是通用模型。下面我们用类图来表示,如下所示:


        上图中的各个模块详解如下:

        CClassFactory:用于生产对象,通过类对应的生产者进行生产。
        IClassObjectProducer:生产者接口,抽象出生产者的属性。

        CImpClassObjectProducer:具体的生产者,每个类对应一个生产者,通过类名索引。

        CClassRegisterHelper:注册器,用于注册类的生产者到工厂类中。

        首先我们来实现生产者接口,生产者一般都可以生产对象,销毁对象,获取类名。代码如下:

class IClassObjectProducer{
public:
	virtual void* creatObject() = 0;
	virtual void  releaseObject(void** ppObject) = 0;
	virtual void  getClassName(__out string& strClassName) = 0;
};
       有了生产者接口,我们就可以得到每个类的生产者的定义,因为每个类的生产者都具有相同的接口,我们可以用宏来实现具体的生产者,并将这个生产者注册到类中,用类名与对应的生产者形成映射关系,其实就是一一对应关系,一个类名对应一个生产者,宏定义如下:

#define  REGISTER_FACTORY_CLASS(ClassName) \
class C##ClassName##producer : public IClassObjectProducer \
{ \
public: \
	   virtual void* creatObject(){ return new ClassName;}\
	   virtual void  releaseObject(void** ppObject){ if ( ppObject != NULL && *ppObject != NULL){ ClassName* p = (ClassName*)*ppObject; delete(p); }}\
	   virtual void  getClassName(__out string& strClassName){ strClassName = #ClassName;}\
}; \
static CClassRegisterHelper s_C##ClassName##RegisterHelper( new C##ClassName##producer );
        在以上代码中可以看到最后一条语句,这里巧妙的利用注册器的构造函数,将生产者注册到工厂中。因静态对象在程序入口点前执行,所以无需担心。注册器代码如下:

class  CClassRegisterHelper
{
public:
	CClassRegisterHelper(IClassObjectProducer* pClassObjectProducer);
};
CClassRegisterHelper::CClassRegisterHelper( IClassObjectProducer* pClassObjectProducer )
{
	CClassFactory::registerClassProducer(pClassObjectProducer);
}

       上面通过构造注入依赖,将生产者注册到工厂中,下面我们来看看大头的工厂类的定义。代码如下:

class CClassFactory{
	typedef  map<string, IClassObjectProducer*> FactoryProducerMap;    //类名与生产者对应的map
	typedef pair<string,IClassObjectProducer*> FactoryProducerPair;
public:
	static void* createClassObject(__in const string& strClassName);
	static bool  releaseClassObject(__inout void** ppObject, __in const string& strClassName);
	static bool  registerClassProducer(__in IClassObjectProducer* pClassObjectProducer); 
	static void  resetClassFactory();
protected:
	static FactoryProducerMap* ms_pMapProducers;

};
void* CClassFactory::createClassObject( __in const string& strClassName )
{
    if ( NULL == ms_pMapProducers )
    {
		ms_pMapProducers = new FactoryProducerMap;
    }

	FactoryProducerMap::iterator it = ms_pMapProducers->find(strClassName);
	void* pObject = NULL;

	if ( it != ms_pMapProducers->end() && it->second != NULL )    //找到类对应的生产者
	{
		pObject = it->second->creatObject();//生产类对象
	}
    
	return pObject;
}

bool CClassFactory::releaseClassObject( __inout void** ppObject, __in const string& strClassName )
{
	if ( NULL == ppObject || NULL == *ppObject || NULL == ms_pMapProducers )
	{
		return false;
	}

	if ( strClassName != "" )
	{
		FactoryProducerMap::iterator it = ms_pMapProducers->find(strClassName);

		if ( it != ms_pMapProducers->end() && it->second != NULL )
		{
			it->second->releaseObject(ppObject);
			*ppObject = NULL;                      //p指向NULL,这里就是传指针的地址才能在函数里面更改指针值,如果传指针,则改变的是副本,原指针不为NULL。
			return true;
		}
	}

    return false;
}

bool CClassFactory::registerClassProducer( __in IClassObjectProducer* pClassObjectProducer )
{
	if ( NULL == ms_pMapProducers )
	{
         ms_pMapProducers = new FactoryProducerMap;
	}
    
	if ( pClassObjectProducer != NULL )
	{
		string strClassName;
		FactoryProducerMap::iterator it;

		pClassObjectProducer->getClassName(strClassName);
		it = ms_pMapProducers->find(strClassName);

		if ( ms_pMapProducers->end() == it )
		{
			ms_pMapProducers->insert(FactoryProducerPair(strClassName, pClassObjectProducer));

		}else   //删除旧的实例,插入新的对象
		{
			delete(it->second);
			it->second = pClassObjectProducer;
		}

		return false;
	}

    
    return true;
}

void CClassFactory::resetClassFactory()
{
    if ( NULL == ms_pMapProducers )
    {
		return;
    }

	FactoryProducerMap::iterator it;

	for ( it = ms_pMapProducers->begin(); it != ms_pMapProducers->end(); ++it )
	{
		delete(it->second);
		it->second = NULL;
	}

	ms_pMapProducers->clear();

	if ( ms_pMapProducers != NULL )
	{
		delete ms_pMapProducers;
		ms_pMapProducers = NULL;
	}
}
各个函数详解:
/*----------------------------------------------------------------------------------------------------------------------------
  函  数:  virtual void* creatObject() = 0;
  访问性:  public 
  修饰词:  virtual
  返回值:  void*:生产的对象的地址。
  参  数:  
  描  述:  产生一个对象,返回对象指针。
  历  史:  2013-8-29:创建
/*----------------------------------------------------------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------------------------------------------------------
  函  数:  virtual void  releaseObject(void** ppObject) = 0;
  访问性:  public 
  修饰词:  virtual
  返回值:  void
  参  数:  void** ppObject:要释放对象指针的地址,传指针地址进去,才能实际操作这个指针,否则操作的是副本,比如传void* p,则传递
           遵循传值调用,传的是p在栈上的副本,这个副本指向原对象,可以delete,但是delete后,对形参p1 = null只是对副本置空,不会
		   对p置空,所有要操作p置空,则传p的地址,则*p取得p的值,然后 *p = null,就可以把实参p在函数内改变。
  描  述:  产生一个对象,返回对象指针。
  历  史:  2013-8-29:创建
/*----------------------------------------------------------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------------------------------------------------------
  函  数:  virtual void  getClassName(__out string& strClassName) = 0;
  访问性:  public 
  修饰词:  virtual
  返回值:  void
  参  数:  __out string& strClassName:获得producer对应的类名。
  描  述:  产生一个对象,返回对象指针。
  历  史:  2013-8-29:创建
/*----------------------------------------------------------------------------------------------------------------------------*/

/*-----------------------------------------------------------------CClassFactory----------------------------------------------

/*----------------------------------------------------------------------------------------------------------------------------
  函  数:  static void* createClassObject(__in const string& strClassName);
  访问性:  public 
  修饰词:  static
  返回值:  void*:生产出的对象指针。
  参  数:  __in const string& strClassName:根据类名创建对象。
  描  述:  根据类名找到producer,通过producer产生一个对象,返回对象指针。
  历  史:  2013-8-29:创建
/*----------------------------------------------------------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------------------------------------------------------
  函  数:  static bool  releaseClassObject(__inout void** ppObject, __in const string& strClassName);
  访问性:  public 
  修饰词:  static
  返回值:  bool:成功是否返回true,反之则为false。
  参  数:  __inout void** ppObject:对象指针的地址,直接对指针操作。
  参  数:  __in const string& strClassName:根据类名释放对象。
  描  述:  根据类名调用producter来销毁对象。
  历  史:  2013-8-29:创建
/*----------------------------------------------------------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------------------------------------------------------
  函  数:  static bool  registerClassProducer(__in IClassObjectProducer* pClassObjectProducer); 
  访问性:  public 
  修饰词:  static
  返回值:  bool:成功是否返回true,反之则为false。
  参  数:  __in IClassObjectProducer* pClassObjectProducer:producer对象指针。
  描  述:  将producer注册到工厂中的map,producer本事有获取类名的接口,所有这里不用提供类名。
  历  史:  2013-8-29:创建
/*----------------------------------------------------------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------------------------------------------------------
  函  数:  static void  resetClassFactory();
  访问性:  public 
  修饰词:  static
  返回值:  void
  参  数:  
  描  述:  清除map中的所有项。
  历  史:  2013-8-29:创建
/*----------------------------------------------------------------------------------------------------------------------------*/

/*------------------------------------------------------------CClassRegisterHelper--------------------------------------------

/*----------------------------------------------------------------------------------------------------------------------------
  函  数:  CClassRegisterHelper(IClassObjectProducer* pClassObjectProducer);
  访问性:  public 
  修饰词:  
  返回值:  
  参  数:  IClassObjectProducer* pClassObjectProducer:注册producer到map中。
  描  述:  构造函数,利用构造函数来调用Factory的注册函数,将producer对象注册到Factory中。此对象将声明为全局变量,这样就能在程序
           运行前构造出producer。
  历  史:  2013-8-29:创建
/*----------------------------------------------------------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------------------------------------------------------
  宏    :  REGISTER_FACTORY_CLASS(ClassName)
  说  明:  此宏用于定义参数ClassName对应的producer。 并声明一个全局的CClassRegisterHelper
/*----------------------------------------------------------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------------------------------------------------------
  原理:  在类的头文件中添加:REGISTER_FACTORY_CLASS(ClassName),表示将这个类加到工厂中。编译时将宏展开,得到类的producer。这时
         实例化一个全局变量CClassRegisterHelper。在CClassRegisterHelper的构造中实例化一个producer,调用Factory的registerClassP
		 roducer(),将实例化在全局区的producer注册到Factory的map中,将类名ClassName与producer对应起来。在客户的就可以调用Factory
		 的相应函数来生产,销毁对象了。
/*----------------------------------------------------------------------------------------------------------------------------*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值