设计模式学习不在于一蹴而就,而在于持续积累与反复思考。本篇作为设计模式学习计划的第一天内容,旨在通过结构化讲解帮助读者牢牢掌握单例模式的核心思想与使用方法。后续每日将采用"回顾昨日知识 + 学习新模式 "的方式推进,这样开篇,一方面帮助读者快速回忆 Day 1,另一方面也自然引出了 Day 2 的学习重点。
一、设计意图
在某些应用中,我们希望某个类在系统中只存在一个实例。例如:
- 配置管理类
- 日志系统
- 数据库连接池
- 线程池
这些对象往往资源占用大,或维护全局状态,必须唯一。这时,就需要单例模式来帮忙。
二、结构与类图
单例模式的结构非常简单,包含三个关键要素:
- 私有构造函数:阻止外部实例化
- 静态私有指针:保存唯一实例
- 公开的静态方法:对外提供访问点
简化类图如下:
+-----------------+
| Singleton |
+-----------------+
| - instance |
| - Singleton() |
+-----------------+
| + getInstance() |
+-----------------+
三、C++实现方式
✅ C++11 懒汉式(推荐)
cpp
class Singleton {
private:
Singleton() {} // 私有构造函数
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton* getInstance() {
static Singleton instance; // 局部静态变量,线程安全(C++11)
return &instance;
}
};
🚫 不推荐写法(线程不安全懒汉式)
cpp
class Singleton {
private:
static Singleton* instance;
Singleton() {}
public:
static Singleton* getInstance() {
if (!instance)
instance = new Singleton(); // 多线程可能创建多个实例
return instance;
}
};
Singleton* Singleton::instance = nullptr;
四、适用场景
场景 | 理由 |
---|---|
日志系统 | 统一记录日志,避免多个实例重复创建文件等 |
配置文件加载器 | 系统初始化时加载一次,后续使用共享配置 |
数据库连接池 | 控制连接数量,避免资源滥用 |
操作系统核心对象 | 比如内核、任务调度器等只应存在一个实例 |
五、优缺点分析
✅ 优点:
- 控制类的实例数量(仅一个)
- 提供全局访问点
- 延迟加载、节省资源(懒汉式)
❌ 缺点:
- 不易扩展,可能违反开闭原则
- 在多线程环境下实现需特别小心
- 单例对象难以单元测试(耦合高)
六、今日练习题
✍️ 题目一:
请使用 C++ 实现一个线程安全的单例类 Logger
,要求:
- 构造函数私有
- 拷贝构造和赋值运算符禁用
- 多线程环境中保证唯一性
✍️ 题目二:
请思考并回答:
单例模式是否违反了开闭原则?如果是,原因是什么?
七、滚动复习:
由于今天是学习计划的第一天,无需复习内容。从明天起,每篇文章末尾将加入1~2个前面学过的设计模式重点回顾。
八、小结
单例模式是一种结构简单、思想明确的设计模式。它在实际工程中广泛应用,但也容易被滥用或实现不当。务必根据场景是否需要唯一实例来选择是否使用。
明日主题预告:工厂方法模式(Factory Method) ------ 更灵活的对象创建方式,敬请期待!