单例的弊端
回顾以下单例模式的定义:保证一个类仅有一个实例,并提供一个该实例的全局访问点。
这里提到了「 **全局 」**两个字,可见单例本质就是一种全局资源。
那自然无法避免全局对象的各种弊端;
如:
- 滥用问题,任何人都可以使用该资源
- 生命周期问题,该对象从构造起贯穿整个应用程序种
- 缺乏灵活性,全局资源的修改将涉及直接使用和间接使用的全部模块
- 测试的困难,使用全局资源点不方便进行单元测试
单例的避免
其实解决的方法有不少,这里展示注入的方式对取消单例。
怎么注入呢?通常可以通过对象的构造函数,或者 set 方法将原先的单例对象注入到目标对象中。

单例的方式
class MySql {
public:
static MySql& instance() {
static MySql s_instance;
return s_instance;
}
public:
void foo() {}
private:
MySql() = default;
~MySql() = default;
private:
MySql(const MySql&) = delete;
MySql& operator=(const MySql&) = delete;
};
// 依赖于单例
class Widget {
private:
MySql* sql;
public:
Widget() {
sql = &MySql::instance();
}
void refresh() {
sql->foo();
// TODO
}
};
int main() {
Widget widget;
widget.refresh();
// TODO
}
注入的方式
class MySql {
public:
MySql() = default;
~MySql() = default;
public:
void foo() {}
};
// 完全独立的模块
class Widget {
private:
MySql* sql;
public:
Widget(MySql* sql) : sql(sql) {
}
void refresh() {
sql->foo();
// TODO
}
};
int main() {
MySql sql;
// 通过构造器进行注入
Widget widget(&sql);
widget.refresh();
// TODO
}