本系列的前两篇文章主要是利用c++语言本身的特点,使我们设计生成的类能够自我保证:从该类实例化的对象数目不会超过某个限定值。
其实在我们项目开发中,大多是情况下都是编程者设计的类仅供自己使用,并不需要考虑第三方二次开发使用的情况。如果是这样,我们往往可以使用一些共同的约定来保证对象生成数量的唯一。下面我列举一个我在实际项目开发中的例子。
开发背景与分析:
项目中需要一个专门提供服务功能的函数集合供各个工程模块使用。假如这些服务函数的实现体固定不变,我们完全可以将其存放在一个namespace中;当然也可以采用类包装的方式,把所有函数声明为类的静态成员。但是实际情况中,这些服务函数的实现体需要因为使用环境的不同而发生一些变化。因此,我首先定义出一个接口类,提供各种虚拟的服务函数接口,以便于针对各种不同的使用环境继承实现出不同的函数体。
/// Service接口定义基本的服务功能函数供其它模块使用
/// 同一时刻最多允许有一个服务类实例被注册使用
/// </summary>
class Service
{
public:
/// <summary>
/// 返回注册的服务类实列
/// 如果没有服务类实例,返回Null
/// </summary>
/// <returns>指向服务类实例的指针</returns>
static Service * getService()
{
return pServiec;
}
/// <summary>
/// 注册服务类实例
/// </summary>
/// <param name = "srv">指向服务类实例的指针</param>
static void setService(Service * srv)
{
pServiec = srv;
}
//多个提供具体服务的纯虚函数
virtual bool serviceFunc1() = 0;
//...
private:
static Service * pServiec;
};
这个接口类保存一个静态指针代表当前注册使用的服务类,并通过SET/GET方法来实现服务的加载和访问。注意:这里使用的是静态指针而不是静态类对象。原因是:我们不知道具体的服务实现类究竟是什么。
class ServiceImpOne: public Service
{
public:
ServiceImpOne(){}
virtual ~ServiceImpOne(){}
bool serviceFunc1()
{
//函数的实现内容,操作成功返回true,否则返回false
}
//...
};
class ServiceImpTwo: public Service
{
public:
ServiceImpTwo(){}
virtual ~ServiceImpTwo(){}
bool serviceFunc1()
{
//函数的实现内容,操作成功返回true,否则返回false
}
//...
};
既然是通过指针指向注册的服务对象,就必须要考虑对象的生成和释放,要注意避免内存泄漏的发生。
当我们需要注册某个新服务,我们首先需要取消旧的服务。
if (pS != NULL) {
delete pS;
Service::setService(NULL);
}
(注意:指针指向的地址和指针指向的内容不同,因此要在delete指针之后将指针赋为空。否则pS!=NULL的判断就会出问题)然后NEW出相应的服务类对象,并这册保存它
ServiceImpTwo * pS = new ServiceImpTwo ();
Service::setService(pS);
}
最终,别忘了在程序结束的地方检查并释放函数指针。
如果在程序的某些地方我们并不希望有任何服务被调用,但是被引用的代码却存在大量服务调用那该怎么办?如果注册服务不为空,则会导致不需要甚至是错误的程序执行;如果注册服务为空。用户使用“if(Service::getService()->serviceFunc1())”必将引发crash。看起来,我们要大面积改动代码。然而,我们这里使用了一个巧妙的方法。从服务接口类继承了一个新的类--ServiceForEnabler。这个类仅仅是为了不改动原有程序的情况下还能够使整个流程enable。这样一来,我们只需在程序的开始处,用一个ServiceForEnabler类对象来注册服务就可以了。
{
public:
ServiceForEnabler(){}
~ServiceForEnabler(){}
bool serviceFunc1()
{
//直接返回false,代表不成功的调用
}
//...
}
纵观全局,我们完全保证了服务对象的唯一存在。需要注意的是:这里不是以某一个服务对象贯穿始终,而是多个不同种类服务对象的交替存在。