你不知道的小知识单例模式(是什么,为什么,怎么用)

430 阅读5分钟

单例模式是什么???

单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点。通俗地说,就是我们只允许某个类创建一个对象,并且所有需要这个对象的地方都能访问到它。

单例模式的核心思想

  1. 唯一实例:确保某个类只有一个实例。
  2. 全局访问点:提供一个可以访问这个实例的全局访问点。

单例模式的实现步骤

  1. 私有化构造函数:防止外部通过构造函数创建实例。
  2. 静态变量存储唯一实例
  3. 提供静态方法获取实例:该方法会在实例不存在时创建实例,存在时返回已存在的实例。

为什么要做成单例模式???单例模式有什么好处???

1. 资源管理

例子:数据库连接

单例模式的好处:

在许多应用中,数据库连接是一个非常耗资源的操作。每次连接到数据库都需要消耗时间和资源。如果我们每次需要数据库连接时都创建一个新的连接,会导致大量的资源浪费。

class DatabaseConnection {
  constructor() {
    if (DatabaseConnection.instance) {
      return DatabaseConnection.instance;
    }
    this.connection = this.createConnection();
    DatabaseConnection.instance = this;
  }

  createConnection() {
    // 模拟创建数据库连接的过程
    console.log("Creating a new database connection...");
    return {};
  }

  static getInstance() {
    if (!DatabaseConnection.instance) {
      DatabaseConnection.instance = new DatabaseConnection();
    }
    return DatabaseConnection.instance;
  }
}

// 测试 DatabaseConnection 类
const db1 = DatabaseConnection.getInstance();
const db2 = DatabaseConnection.getInstance();

console.log(db1 === db2); // true

不使用单例模式的坏处:

如果不使用单例模式,每次我们需要数据库连接时都会创建一个新的连接,这不仅浪费资源,还可能导致连接数超过数据库的限制,进而导致应用崩溃。

class DatabaseConnection {
  constructor() {
    this.connection = this.createConnection();
  }

  createConnection() {
    console.log("Creating a new database connection...");
    return {};
  }
}

// 测试 DatabaseConnection 类
const db1 = new DatabaseConnection();
const db2 = new DatabaseConnection();

console.log(db1 === db2); // false

2. 全局状态

例子:应用程序配置

单例模式的好处:

在应用程序中,我们通常需要一个全局的配置对象来管理应用的配置。这些配置需要在应用的不同部分共享和修改。如果每个地方都创建一个新的配置对象,会导致配置的不一致。

class Configuration {
  constructor() {
    if (Configuration.instance) {
      return Configuration.instance;
    }
    this.settings = {};
    Configuration.instance = this;
  }

  set(key, value) {
    this.settings[key] = value;
  }

  get(key) {
    return this.settings[key];
  }

  static getInstance() {
    if (!Configuration.instance) {
      Configuration.instance = new Configuration();
    }
    return Configuration.instance;
  }
}

// 测试 Configuration 类
const config1 = Configuration.getInstance();
config1.set('apiBaseUrl', 'https://api.example.com');

const config2 = Configuration.getInstance();
console.log(config2.get('apiBaseUrl')); // 'https://api.example.com'

console.log(config1 === config2); // true

不使用单例模式的坏处:

如果不使用单例模式,不同部分的代码会创建自己的配置对象,这会导致配置不一致,进而导致应用程序行为异常。

class Configuration {
  constructor() {
    this.settings = {};
  }

  set(key, value) {
    this.settings[key] = value;
  }

  get(key) {
    return this.settings[key];
  }
}

// 测试 Configuration 类
const config1 = new Configuration();
config1.set('apiBaseUrl', 'https://api.example.com');

const config2 = new Configuration();
console.log(config2.get('apiBaseUrl')); // undefined

console.log(config1 === config2); // false

3. 控制实例数量

例子:日志记录器

单例模式的好处:

在应用中,我们可能需要一个日志记录器来记录日志。我们希望只有一个日志记录器实例来确保日志的一致性和顺序。

class Logger {
  constructor() {
    if (Logger.instance) {
      return Logger.instance;
    }
    this.logs = [];
    Logger.instance = this;
  }

  log(message) {
    const timestamp = new Date().toISOString();
    this.logs.push(`[${timestamp}] ${message}`);
    console.log(`[${timestamp}] ${message}`);
  }

  getLogs() {
    return this.logs;
  }

  static getInstance() {
    if (!Logger.instance) {
      Logger.instance = new Logger();
    }
    return Logger.instance;
  }
}

// 测试 Logger 类
const logger1 = Logger.getInstance();
logger1.log('First log message');

const logger2 = Logger.getInstance();
logger2.log('Second log message');

console.log(logger1 === logger2); // true
console.log(logger1.getLogs()); 

不使用单例模式的坏处:

如果不使用单例模式,每次需要记录日志时都创建一个新的日志记录器实例,这会导致日志分散在不同的实例中,难以追踪和管理。

class Logger {
  constructor() {
    this.logs = [];
  }

  log(message) {
    const timestamp = new Date().toISOString();
    this.logs.push(`[${timestamp}] ${message}`);
    console.log(`[${timestamp}] ${message}`);
  }

  getLogs() {
    return this.logs;
  }
}

// 测试 Logger 类
const logger1 = new Logger();
logger1.log('First log message');

const logger2 = new Logger();
logger2.log('Second log message');

console.log(logger1 === logger2); // false
console.log(logger1.getLogs()); 
console.log(logger2.getLogs()); 

总结

  • 资源管理:单例模式可以避免创建多个耗资源的对象,节省资源。
  • 全局状态:单例模式确保全局状态一致,避免状态不一致的问题。
  • 控制实例数量:单例模式控制实例数量,避免多个实例导致的数据不一致和管理困难。

怎么去使用单例模式???(一个实战小demo)

项目背景

假设我们正在开发一个Web应用程序,需要管理用户会话(例如,用户登录状态、会话信息等)。我们希望在应用程序的任何地方都能访问和修改当前用户的会话信息,并确保会话信息的一致性。

项目需求

  1. 单一实例:整个应用程序中只有一个会话管理器实例。
  2. 管理用户会话:能够设置和获取当前用户的会话信息。
  3. 销毁会话:能够清除当前用户的会话信息。

设计与实现

我们将创建一个 SessionManager 类来实现这些需求。这个类将使用单例模式确保只有一个实例。

1. 私有化构造函数

防止外部通过 new 关键字创建多个实例。

2. 静态变量存储实例

存储单例实例。

3. 静态方法获取实例

提供一个全局访问点获取实例。

4. 设置和获取会话信息

提供方法设置和获取当前用户的会话信息。

5. 销毁会话

提供方法清除当前用户的会话信息。

实现代码

class SessionManager {
  constructor() {
    if (SessionManager.instance) {
      return SessionManager.instance;
    }
    this.session = null;
    SessionManager.instance = this;
  }

  setSession(user) {
    this.session = user;
  }

  getSession() {
    return this.session;
  }

  destroySession() {
    this.session = null;
  }

  static getInstance() {
    if (!SessionManager.instance) {
      SessionManager.instance = new SessionManager();
    }
    return SessionManager.instance;
  }
}

// 测试 SessionManager 类
const session1 = SessionManager.getInstance();
session1.setSession({ username: 'Alice', role: 'admin' });

const session2 = SessionManager.getInstance();
console.log(session2.getSession());

session2.destroySession();
console.log(session1.getSession()); // null

console.log(session1 === session2); // true

解释

  • SessionManager 类:构造函数内部检查 SessionManager.instance 是否存在,如果存在就返回这个实例。如果不存在,则创建实例并赋值给 SessionManager.instance
  • 静态方法 getInstance:确保 SessionManager 只有一个实例。
  • setSession 方法:设置当前用户的会话信息。
  • getSession 方法:获取当前用户的会话信息。
  • destroySession 方法:清除当前用户的会话信息。

通过这个简单的用户会话管理器示例,你可以清楚地看到单例模式的应用场景及其实现方式。如果有任何问题或需要更详细的解释,随时告诉我!