设计模式-单例模式

日常开发,单例设计模式是解决常用的设计模式之一。今天我们来深入探讨单例模式(Singleton Pattern),这个看似简单却应用广泛的设计模式。

什么是单例模式

单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取该实例。

核心特征:

  • 全局唯一性:整个应用程序中只存在一个实例
  • 延迟实例化:通常在第一次使用时才创建实例
  • 全局访问:提供静态方法获取实例

单例模式的几种实现方式

1. 饿汉式(Eager Initialization)

js 复制代码
class EagerSingleton {
  private static instance = new EagerSingleton();
  
  private constructor() {}
  
  static getInstance(): EagerSingleton {
    return EagerSingleton.instance;
  }
}

特点: 类加载时就创建实例,线程安全但可能浪费内存。

2. 懒汉式(Lazy Initialization)

js 复制代码
class LazySingleton {
  private static instance: LazySingleton;
  
  private constructor() {}
  
  static getInstance(): LazySingleton {
    if (!LazySingleton.instance) {
      LazySingleton.instance = new LazySingleton();
    }
    return LazySingleton.instance;
  }
}

特点: 第一次使用时才创建,节省内存但需考虑线程安全。

3. 双重检查锁定(Double-Checked Locking)

js 复制代码
class DoubleCheckSingleton {
  private static instance: DoubleCheckSingleton;
  private static creating = false;
  
  private constructor() {}
  
  static getInstance(): DoubleCheckSingleton {
    if (!DoubleCheckSingleton.instance) {
      if (!DoubleCheckSingleton.creating) {
        DoubleCheckSingleton.creating = true;
        DoubleCheckSingleton.instance = new DoubleCheckSingleton();
        DoubleCheckSingleton.creating = false;
      }
    }
    return DoubleCheckSingleton.instance;
  }
}

前端业务中的真实使用场景

1. 用户信息管理

js 复制代码
// 实名信息单例对象
export class UserInfo {
  private static instance: UserInfo;

  id: string = '';
  account: string = '';
  realname: string | null = null;
  // ... 其他属性

  private constructor() {
    // 私有构造函数防止外部实例化
  }

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

  updateFromResponse(data: any): void {
    this.id = data.id || this.id;
    this.account = data.account || this.account;
    // ... 
  }

  clear(): void {
    this.id = '';
    this.account = '';
    // ... 
  }
}

// 使用
const userInfo = UserInfo.getInstance();
userInfo.updateFromResponse(apiResponse);

2. 全局配置管理

js 复制代码
class AppConfig {
  private static instance: AppConfig;
  
  private config = {
    apiBaseUrl: '',
    theme: 'light',
    language: 'zh-CN'
  };

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

  setConfig(key: string, value: any) {
    this.config[key] = value;
  }

  getConfig(key: string) {
    return this.config[key];
  }
}

3. 事件总线

js 复制代码
class EventBus {
  private static instance: EventBus;
  private events: Map<string, Function[]> = new Map();

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

  on(event: string, callback: Function) {
    if (!this.events.has(event)) {
      this.events.set(event, []);
    }
    this.events.get(event)!.push(callback);
  }

  emit(event: string, data?: any) {
    const callbacks = this.events.get(event);
    if (callbacks) {
      callbacks.forEach(callback => callback(data));
    }
  }
}

4. 缓存管理器

js 复制代码
class CacheManager {
  private static instance: CacheManager;
  private cache = new Map<string, { data: any, expiry: number }>();

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

  set(key: string, data: any, ttl = 5 * 60 * 1000) {
    this.cache.set(key, {
      data,
      expiry: Date.now() + ttl
    });
  }

  get(key: string) {
    const item = this.cache.get(key);
    if (!item) return null;
    
    if (Date.now() > item.expiry) {
      this.cache.delete(key);
      return null;
    }
    
    return item.data;
  }
}

SSM框架中的单例模式

最近又想复习一下SSM,所以也顺带讲一下SSM中的单例模式

1. Spring 中的单例

Spring 容器中的 Bean 默认是单例的(只是默认):

js 复制代码
@Service
public class UserService {
    // Spring 容器保证全局唯一
}

@Component
@Scope("singleton") // 显式声明单例
public class ConfigService {
    
}

2. MyBatis 中的单例

js 复制代码
// SqlSessionFactory 通常作为单例使用
public class MyBatisConfig {
    private static SqlSessionFactory sqlSessionFactory;
    
    public static SqlSessionFactory getSqlSessionFactory() {
        if (sqlSessionFactory == null) {
            // 初始化逻辑
        }
        return sqlSessionFactory;
    }
}

3. 数据库连接池

js 复制代码
public class DatabasePool {
    private static volatile DatabasePool instance;
    private DataSource dataSource;
    
    private DatabasePool() {
        // 初始化连接池
    }
    
    public static DatabasePool getInstance() {
        if (instance == null) {
            synchronized (DatabasePool.class) {
                if (instance == null) {
                    instance = new DatabasePool();
                }
            }
        }
        return instance;
    }
}

单例模式的优缺点

优点

  • 内存节省:避免重复创建对象
  • 全局访问:提供统一的访问入口
  • 状态共享:便于在不同模块间共享状态(这个其实在react中也是非常好用的)
  • 延迟加载:按需创建,提高启动性能

单例模式在开发中还是非常常见的,至少一个项目中是有好几处的,然后了解设计模式也能提高编程思维,合适的时候用合适的算法,不要过度使用,明明不适合非常那些写,那么写出来也是十扇,永远以确保代码的可维护性和可测试性为标准编写代码。

相关推荐
FungLeo6 小时前
成为全栈·React 管理后台篇·总复盘:从“接口能调通”到“后台值得使用”
前端·react.js·前端框架·项目复盘·成为全栈
Csvn7 小时前
并发模式:让渲染学会排队、插队和让路
前端
可乐鸡翅yeah_7 小时前
新手梳理:M3U8 线上问题,哪些是前端锅,哪些是后端锅
前端·ios·音视频·实时音视频·m3u8·音视频在线播放
Flynt7 小时前
Linear 用 1000 个 PR 换掉 styled-components,我写了 200 个按钮,把这笔账复现了一遍
前端·css·preact
JavaGuide8 小时前
NVIDIA 又开源了!这次给 AI Agent 加上权限管控
前端·后端
excel8 小时前
prisma 如何处理数据库竞态
前端·数据库·后端
莪_幻尘10 小时前
Skill 体检:30 个 Skill 全凭感觉?体检器先自曝了 8 个“假 0 分
前端·人工智能·llm
风骏时光牛马10 小时前
AI模型综合能力评测:性能、指令遵循与多场景实测对比
前端
Frag0ut10 小时前
Chrome与Chromium内核浏览器在Windows 11上的新特性全景解析
前端·chrome·windows·web安全·chromium·gemini ai·playready drm
hiahiahia12310 小时前
实现完整 Tool Dispatcher
开发语言·前端