最近在代码阅读中看到了一种可继承的单例实现,对使用者而言通过单例基类到定义的静态接口获取实例,而不必关心实际的单例实现。灵活性上,可通过继承的重写虚接口的方式实现单例功能的扩展。基础代码如下:
/* TestSingleton.h */
#include <iostream>
using namespace std;
#define ClassName(klass) (#klass)
class SingletonBase;
namespace
{
SingletonBase* ptr_single_instance = nullptr;
}
class SingletonBase
{
public:
SingletonBase();
~SingletonBase();
// 虚接口
virtual void TestSingleton();
static SingletonBase* Get();
};
SingletonBase::SingletonBase()
{
ptr_single_instance = this;
}
SingletonBase::~SingletonBase()
{
}
SingletonBase* SingletonBase::Get()
{
return ptr_single_instance;
}
void SingletonBase::TestSingleton()
{
cout << ClassName(SingletonBase) << std::endl;
}
class SingletonImp1 : public SingletonBase
{
public:
SingletonImp1();
~SingletonImp1();
virtual void TestSingleton() override;
};
SingletonImp1::SingletonImp1()
: SingletonBase()
{
}
SingletonImp1::~SingletonImp1()
{
}
void SingletonImp1::TestSingleton()
{
cout << ClassName(SingletonImp1) << std::endl;
}
void GoTest()
{
//使用者实际选择创建何种单例,而不是在Get接口中new
SingletonImp1* ptr_single_imp = new SingletonImp1();
SingletonBase::Get()->TestSingleton();
}
当然在扩展性增强的同时也带来问题:相对于传统单例实现方式–在Get接口中new一个当前类型的实例,此种单例实例的创建暴露给了使用者,需要由使用者事先选择并创建指定子类型的实例,然后其他地方获取单例对象使用。
单例使用心得
单例便于单例类型中的接口调用,避免了类型上功能相互调用时参数传递的麻烦。但单例使用也存在一定的问题:
1. 单例的生命周期不易控制;
2. 单例使得类型间的依赖更加隐蔽且难以管理;
3. 单例中的定义的功能接口不易继承扩展:例如在项目开发中错误信息提示窗口统一在窗口右上角提示。实现上可通过定义消息提示的单例对象,在程序出错处调用单例对象的接口完成消息提示。随着功能的演进,又有了新的需求:消息不仅要在窗口右上提示,部分错误要以弹模态框的方式提示。因实例唯一,在接口不动的情况下通过继承的方式进行扩展显然是做不到的,而且单例接口变动带来的改动量也相当可观。所以功能接口功能比较稳定可封装为单例,如若不然就要为以后的功能变动买单。