前言
最近整理了一份设计模式手册:从入门到精通的实用指南。
坦白说,我对网上那些过于理论化的教程感到有些失望。于是决定亲自动手,从基础概念到实际应用,把常用的设计模式都梳理了一遍。每种模式不仅包含核心原理,还附带了真实的代码示例,希望能帮助大家更好地理解和运用这些模式。
如果你正在学习软件设计,或想提升架构能力,不妨看看这份指南。也许对你有所启发,也可能觉得平平无奇,但这是我的一点小小心意。
欢迎随时分享你的想法,让我们一起探讨,共同进步!
定义
单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取该实例。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。单例模式不仅限制了一个类只能有一个实例,还负责自行创建这个实例。
单例模式的核心概念包括:
-
私有构造函数:确保类不能从外部被实例化。
-
私有静态实例:类的唯一实例作为类的私有静态成员存储。
-
公共静态访问方法:提供一个全局访问点来获取类的唯一实例。
在JavaScript中,由于语言的动态性和模块化特性,单例模式的实现可能会有所不同。通常,我们可以通过模块模式、闭包或ES6的类语法来实现单例。
代码实现
class Singleton {
constructor() {
if (Singleton.instance) {
return Singleton.instance;
}
this.data = Math.random();
Singleton.instance = this;
}
static getInstance() {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
getData() {
return this.data;
}
}
// 防止实例化
Object.freeze(Singleton);
使用示例
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // 输出: true
console.log(instance1.getData() === instance2.getData()); // 输出: true
详细解释
-
私有构造函数:构造函数检查是否已存在实例。如果存在,则返回现有实例;否则,创建新实例。
-
静态实例:
Singleton.instance
是静态属性,存储唯一实例。 -
静态访问方法:
getInstance()
是获取实例的唯一途径,确保只创建一个实例。 -
延迟初始化:实例在首次调用
getInstance()
时才被创建,实现懒加载。 -
防止修改:使用
Object.freeze()
防止 Singleton 类被修改。
优点
-
资源节约:只创建一个实例,节省了系统资源,特别是对于那些创建和销毁开销较大的对象。
-
全局访问点:提供了对唯一实例的全局访问点,方便对实例进行控制和管理。
-
严格控制:可以严格控制客户端如何访问实例以及何时访问实例。
-
灵活性:允许对实例的数量进行灵活控制。可以在运行时判断是否需要多个实例。
-
延迟实例化:单例模式可以延迟实例化,即在第一次使用时才实例化对象,有助于提高性能。
-
避免资源竞争:在多线程环境中,可以避免对共享资源的多重占用。
-
跨模块共享:在模块化的应用程序中,单例可以作为跨模块共享状态或功能的有效方式。
缺点
-
违反单一职责原则:单例类既要管理自身的职责,又要确保只有一个实例,可能导致类的职责过重。
-
隐藏依赖关系:单例模式可能隐藏了类之间的依赖关系,使得系统结构不够清晰,增加了系统的耦合度。
-
测试困难:单例模式的静态特性使得单元测试变得困难,因为许多测试框架依赖于继承和多态来完成测试。
-
扩展性差:单例类通常难以扩展。如果需要扩展单例类,往往需要修改原有类的代码。
-
与面向对象设计原则冲突:单例模式在某种程度上违背了一些面向对象的原则,如开闭原则。
-
并发问题:在多线程环境下,如果不谨慎处理,可能会出现多个实例的情况。
-
全局状态:引入了全局状态到应用程序中,可能导致代码的紧耦合和难以维护。
使用场景
1. 全局状态管理
例如在前端框架中的状态管理器(如Redux或Vuex)。
class Store {
constructor() {
if (Store.instance) return Store.instance;
this.state = {};
Store.instance = this;
}
static getInstance() {
if (!Store.instance) {
Store.instance = new Store();
}
return Store.instance;
}
setState(key, value) {
this.state[key] = value;
}
getState(key) {
return this.state[key];
}
}
2. 数据库连接池
管理数据库连接,确保只有一个连接池实例。
class DatabasePool {
constructor() {
if (DatabasePool.instance) return DatabasePool.instance;
this.pool = [];
DatabasePool.instance = this;
}
static getInstance() {
if (!DatabasePool.instance) {
DatabasePool.instance = new DatabasePool();
}
return DatabasePool.instance;
}
getConnection() {
// 实现获取连接的逻辑
}
releaseConnection(connection) {
// 实现释放连接的逻辑
}
}
3. 日志记录器
确保应用程序只使用一个日志记录器实例。
class Logger {
constructor() {
if (Logger.instance) return Logger.instance;
this.logs = [];
Logger.instance = this;
}
static getInstance() {
if (!Logger.instance) {
Logger.instance = new Logger();
}
return Logger.instance;
}
log(message) {
const timestamp = new Date().toISOString();
this.logs.push(`${timestamp}: ${message}`);
console.log(`${timestamp}: ${message}`);
}
getLogs() {
return this.logs;
}
}
4. 配置管理
/**
* 配置管理器
* 用于集中管理应用程序的全局配置
* 确保整个应用中只有一个配置实例,避免配置不一致
*/
class ConfigManager {
constructor() {
this.config = {};
}
static getInstance() {
if (!ConfigManager.instance) {
ConfigManager.instance = new ConfigManager();
}
return ConfigManager.instance;
}
setConfig(key, value) {
this.config[key] = value;
}
getConfig(key) {
return this.config[key];
}
}
const configManager = ConfigManager.getInstance();
5. 缓存管理
/**
* 缓存管理器
* 实现一个全局的缓存系统,用于存储和检索数据
* 单例模式确保所有组件使用同一个缓存实例,提高效率
*/
class CacheManager {
constructor() {
this.cache = new Map();
}
static getInstance() {
if (!CacheManager.instance) {
CacheManager.instance = new CacheManager();
}
return CacheManager.instance;
}
set(key, value, ttl) {
this.cache.set(key, {
value,
expiry: Date.now() + ttl
});
}
get(key) {
const item = this.cache.get(key);
if (item && item.expiry > Date.now()) {
return item.value;
}
this.cache.delete(key);
return null;
}
}
const cacheManager = CacheManager.getInstance();
6. 线程池
/**
* 线程池管理器
* 在支持Web Workers的环境中管理线程池
* 单例模式确保整个应用使用同一个线程池,避免资源浪费
*/
class ThreadPool {
constructor(size) {
this.size = size;
this.tasks = [];
this.workers = [];
}
static getInstance(size = 4) {
if (!ThreadPool.instance) {
ThreadPool.instance = new ThreadPool(size);
}
return ThreadPool.instance;
}
addTask(task) {
this.tasks.push(task);
this.runTask();
}
runTask() {
if (this.workers.length < this.size && this.tasks.length > 0) {
const worker = new Worker('worker.js');
this.workers.push(worker);
const task = this.tasks.shift();
worker.postMessage(task);
}
}
}
const threadPool = ThreadPool.getInstance();
7. 设备管理器
/**
* 设备管理器
* 用于管理硬件设备的连接和状态
* 单例模式确保所有组件通过同一个实例访问设备,避免冲突
*/
class DeviceManager {
constructor() {
this.devices = new Map();
}
static getInstance() {
if (!DeviceManager.instance) {
DeviceManager.instance = new DeviceManager();
}
return DeviceManager.instance;
}
connectDevice(id, device) {
this.devices.set(id, device);
}
disconnectDevice(id) {
this.devices.delete(id);
}
getDevice(id) {
return this.devices.get(id);
}
}
const deviceManager = DeviceManager.getInstance();
8. 游戏管理器
/**
* 游戏管理器
* 管理游戏的全局状态,如分数和关卡
* 单例模式确保游戏中只有一个状态管理实例,保持数据一致性
*/
class GameManager {
constructor() {
this.score = 0;
this.level = 1;
}
static getInstance() {
if (!GameManager.instance) {
GameManager.instance = new GameManager();
}
return GameManager.instance;
}
increaseScore(points) {
this.score += points;
}
nextLevel() {
this.level++;
}
}
const gameManager = GameManager.getInstance();
9. 文件系统
/**
* 文件系统管理器
* 提供统一的文件操作接口,模拟简单的文件系统
* 单例模式确保整个应用使用同一个文件系统实例,避免数据不一致
*/
class FileSystem {
constructor() {
this.files = new Map();
}
static getInstance() {
if (!FileSystem.instance) {
FileSystem.instance = new FileSystem();
}
return FileSystem.instance;
}
writeFile(path, content) {
this.files.set(path, content);
}
readFile(path) {
return this.files.get(path);
}
deleteFile(path) {
this.files.delete(path);
}
}
const fileSystem = FileSystem.getInstance();
10. 打印机后台处理程序
/**
* 打印机后台处理程序
* 管理打印任务队列,确保打印作业的有序执行
* 单例模式确保只有一个打印队列管理器,避免打印任务冲突
*/
class PrinterSpooler {
constructor() {
this.queue = [];
}
static getInstance() {
if (!PrinterSpooler.instance) {
PrinterSpooler.instance = new PrinterSpooler();
}
return PrinterSpooler.instance;
}
addJob(job) {
this.queue.push(job);
}
processNextJob() {
if (this.queue.length > 0) {
const job = this.queue.shift();
console.log('打印作业:', job);
}
}
}
const printerSpooler = PrinterSpooler.getInstance();
这些场景都利用了单例模式的特性,如全局访问点、资源共享和状态管理等。然而,使用单例模式时需要谨慎考虑其对系统设计的影响,权衡其带来的便利性和可能引入的问题。