基础知识 🌟
单例模式简介
想象一下一个独一无二的超级英雄,在整个宇宙中只能存在一个。这就是单例设计模式的本质! 🦸♂️
为什么需要一个独一无二的英雄? 🤔
以下是常见场景:
- 🎮 一个视频游戏有多个计分板...完全混乱!
- 🏦 多个相同数据库连接...资源浪费!
- ⚙️ 多个不同配置...这是混乱的源头!
单例模式的超能力
- 💪 唯一性 : 只有一个实例,就像哥谭市只有一个蝙蝠侠
- 🌟 全局可见 : 在任何地方都可访问,就像天空中的蝙蝠信号
- 🎯 精确性 : 单一的真实来源,就像魔戒
基础实现
我们英雄的代码
1class Logger {
2 private static instance: Logger;
3 private logCount: number = 0;
4
5 private constructor() {
6 console.log('🚀 Logger 启动中...');
7 }
8
9 public static getInstance(): Logger {
10 if (!Logger.instance) {
11 Logger.instance = new Logger();
12 }
13 return Logger.instance;
14 }
15
16 public log(message: string): void {
17 this.logCount++;
18 console.log(`📝 [日志 #${this.logCount}] ${message}`);
19 }
20
21 public getStats(): string {
22 return `📊 总日志数: ${this.logCount}`;
23 }
24}
简单使用
1const logger = Logger.getInstance();
2logger.log('我们的英雄已准备就绪!');
3console.log(logger.getStats());
高级技巧 🚀
线程安全的单例
在多线程环境中,我们的英雄需要额外的防护!以下是线程安全的实现:
1class ThreadSafeLogger {
2 private static instance: ThreadSafeLogger;
3 private static instanceLock = false;
4 private logs: string[] = [];
5
6 private constructor() {}
7
8 public static getInstance(): ThreadSafeLogger {
9 if (!ThreadSafeLogger.instanceLock) {
10 ThreadSafeLogger.instanceLock = true;
11 if (!ThreadSafeLogger.instance) {
12 ThreadSafeLogger.instance = new ThreadSafeLogger();
13 }
14 ThreadSafeLogger.instanceLock = false;
15 }
16 return ThreadSafeLogger.instance;
17 }
18}
懒加载单例 🦥
1class LazyLogger {
2 private static instance: LazyLogger;
3 private config: object;
4
5 private constructor(config: object) {
6 this.config = config;
7 }
8
9 public static getInstance(config?: object): LazyLogger {
10 if (!LazyLogger.instance && config) {
11 LazyLogger.instance = new LazyLogger(config);
12 }
13 if (!LazyLogger.instance) {
14 throw new Error('首次初始化时Logger需要配置!');
15 }
16 return LazyLogger.instance;
17 }
18}
可测试的单例 🧪
1class TestableLogger {
2 private static instance: TestableLogger;
3
4 private constructor() {}
5
6 public static getInstance(): TestableLogger {
7 if (!TestableLogger.instance) {
8 TestableLogger.instance = new TestableLogger();
9 }
10 return TestableLogger.instance;
11 }
12
13 // 测试专用方法
14 public static resetInstance(): void {
15 TestableLogger.instance = null;
16 }
17}
泛型单例 🎭
1class GenericSingleton<T> {
2 private static instances: Map<string, any> = new Map();
3
4 protected constructor() {}
5
6 public static getInstance<T>(this: new () => T): T {
7 const className = this.name;
8 if (!GenericSingleton.instances.has(className)) {
9 GenericSingleton.instances.set(className, new this());
10 }
11 return GenericSingleton.instances.get(className);
12 }
13}
14
15// 使用方式
16class UserService extends GenericSingleton<UserService> {
17 public getUsers() {
18 return ['user1', 'user2'];
19 }
20}
21
22class ConfigService extends GenericSingleton<ConfigService> {
23 public getConfig() {
24 return { api: 'url' };
25 }
26}
反模式和需要避免的陷阱 ⚠️
- 强耦合
1// 避免这样做 ❌
2class BadSingleton {
3 public static getInstance() {
4 // 代码...
5 }
6 public doDirectDatabaseOperation() {
7 // 直接数据库操作
8 }
9}
10
11// 推荐这样做 ✅
12interface DatabaseOperation {
13 execute(): void;
14}
15
16class GoodSingleton {
17 public static getInstance() {
18 // 代码...
19 }
20 public executeOperation(operation: DatabaseOperation) {
21 operation.execute();
22 }
23}
- 全局可变状态
1// 避免这样做 ❌
2class MutableSingleton {
3 private static instance: MutableSingleton;
4 public globalState: any = {};
5}
6
7// 推荐这样做 ✅
8class ImmutableSingleton {
9 private static instance: ImmutableSingleton;
10 private state: Readonly<any>;
11
12 public getState(): Readonly<any> {
13 return this.state;
14 }
15}
补充模式 🤝
工厂模式 + 单例模式
1interface Logger {
2 log(message: string): void;
3}
4
5class LoggerFactory {
6 private static instance: LoggerFactory;
7 private loggers: Map<string, Logger> = new Map();
8
9 private constructor() {}
10
11 public static getInstance(): LoggerFactory {
12 if (!LoggerFactory.instance) {
13 LoggerFactory.instance = new LoggerFactory();
14 }
15 return LoggerFactory.instance;
16 }
17
18 public getLogger(type: 'console' | 'file'): Logger {
19 if (!this.loggers.has(type)) {
20 this.loggers.set(type, this.createLogger(type));
21 }
22 return this.loggers.get(type);
23 }
24
25 private createLogger(type: 'console' | 'file'): Logger {
26 // 创建特定类型的logger
27 return type === 'console' ? new ConsoleLogger() : new FileLogger();
28 }
29}
高级挑战 🎯
创建一个具备以下特性的单例:
- 线程安全
- 使用懒加载
- 支持测试重置
- 集成版本控制系统
总结 🎬
单例模式是一个强大的设计模式,但需要仔细思考如何实现。基础版本适用于简单场景,但高级版本为复杂应用提供了更多的健壮性和灵活性。
专业提示 💡
- 对于资源密集型操作使用懒加载
- 始终考虑可测试性
- 可能的情况下优先使用依赖注入
- 详细记录单例的使用场景
实践练习 🎮
- 实现一个管理应用程序配置的单例
- 创建一个带有消息队列的线程安全单例
- 开发一个使用单例模式的缓存系统
编码愉快! 🚀