单例模式 (Singleton Pattern) – 设计模式精讲·面试必备

前言

最近整理了一份设计模式精讲: 从入门到精通 – 面试晋升必备指南
 坦白说,我对网上那些过于理论化的教程感到有些失望。于是决定亲自动手,从基础概念到实际应用,把常用的设计模式都梳理了一遍。每种模式不仅包含核心原理,还附带了真实的代码示例,希望能帮助大家更好地理解和运用这些模式。
如果你正在学习软件设计,或想提升架构能力,不妨看看这份指南。也许对你有所启发,也可能觉得平平无奇,但这是我的一点小小心意。
 欢迎随时分享你的想法,让我们一起探讨,共同进步!

定义

单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取该实例。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。单例模式不仅限制了一个类只能有一个实例,还负责自行创建这个实例。

单例模式的核心概念包括:

  1. 私有构造函数:确保类不能从外部被实例化。

  2. 私有静态实例:类的唯一实例作为类的私有静态成员存储。

  3. 公共静态访问方法:提供一个全局访问点来获取类的唯一实例。

在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

详细解释

  1. 私有构造函数:构造函数检查是否已存在实例。如果存在,则返回现有实例;否则,创建新实例。

  2. 静态实例Singleton.instance 是静态属性,存储唯一实例。

  3. 静态访问方法getInstance() 是获取实例的唯一途径,确保只创建一个实例。

  4. 延迟初始化:实例在首次调用 getInstance() 时才被创建,实现懒加载。

  5. 防止修改:使用 Object.freeze() 防止 Singleton 类被修改。

优点

  1. 资源节约:只创建一个实例,节省了系统资源,特别是对于那些创建和销毁开销较大的对象。

  2. 全局访问点:提供了对唯一实例的全局访问点,方便对实例进行控制和管理。

  3. 严格控制:可以严格控制客户端如何访问实例以及何时访问实例。

  4. 灵活性:允许对实例的数量进行灵活控制。可以在运行时判断是否需要多个实例。

  5. 延迟实例化:单例模式可以延迟实例化,即在第一次使用时才实例化对象,有助于提高性能。

  6. 避免资源竞争:在多线程环境中,可以避免对共享资源的多重占用。

  7. 跨模块共享:在模块化的应用程序中,单例可以作为跨模块共享状态或功能的有效方式。

缺点

  1. 违反单一职责原则:单例类既要管理自身的职责,又要确保只有一个实例,可能导致类的职责过重。

  2. 隐藏依赖关系:单例模式可能隐藏了类之间的依赖关系,使得系统结构不够清晰,增加了系统的耦合度。

  3. 测试困难:单例模式的静态特性使得单元测试变得困难,因为许多测试框架依赖于继承和多态来完成测试。

  4. 扩展性差:单例类通常难以扩展。如果需要扩展单例类,往往需要修改原有类的代码。

  5. 与面向对象设计原则冲突:单例模式在某种程度上违背了一些面向对象的原则,如开闭原则。

  6. 并发问题:在多线程环境下,如果不谨慎处理,可能会出现多个实例的情况。

  7. 全局状态:引入了全局状态到应用程序中,可能导致代码的紧耦合和难以维护。

使用场景

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();

这些场景都利用了单例模式的特性,如全局访问点、资源共享和状态管理等。然而,使用单例模式时需要谨慎考虑其对系统设计的影响,权衡其带来的便利性和可能引入的问题。

9. 更多内容推荐

如果您喜欢本站,点击这儿不花一分钱捐赠本站 这些信息可能会帮助到你: 下载帮助 | 报毒说明 | 进站必看
徐白博客平台 » 单例模式 (Singleton Pattern) – 设计模式精讲·面试必备

发表回复

提供最优质的资源集合

立即查看 了解详情