单例模式(Singleton Pattern) 是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。
适用场景——该程序运行过程中只能生成一个实例,以避免对同一资源产生相互冲突的请求。
- 需要一个全局唯一的对象来协调整个系统的行为,如配置管理器,统一管理系统配置。
- 资源共享的情况,如共享的数据库连接池,使用一个数据库对象对数据库进行操作,以维护数据的一致性。
- 控制资源的情况,如管理打印机的使用。
- 日志记录器(Logger):通常在应用程序中只需要一个日志实例,仅使用一个日志类的对象,将多项服务的日志信息按照顺序转储到一个特定的日志文件中。
总之,单例模式的意图就是:
- 确保类有且只有一个对象被创建。
- 为对象提供一个访问点,以使程序可以全局访问该对象。
- 控制共享资源的并行访问。
实现单例模式的一个简单方法:
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
# 初始化代码可以放在这里
return cls._instance
def some_business_logic(self):
# 单例的业务逻辑方法
pass
# 使用示例
s1 = Singleton()
s2 = Singleton()
print(s1 is s2) # 输出: True
__new__
方法来控制实例的创建。hasattr(cls, '_instance')
检查类是否已经有一个实例。_instance
中。改进一下代码:改进版本主要是为了提高代码的可读性和灵活性
# 原始版本
class Singleton(object):
_instance = None
def __new__(cls):
if not hasattr(cls, '_instance'):
cls._instance = super(Singleton, cls).__new__(cls)
return cls._instance
# 改进版本
class SingletonImproved:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
# 初始化代码可以放在这里
return cls._instance
def __init__(self):
# 初始化代码
if not hasattr(self, 'initialized'):
self.initialized = True
# 其他初始化代码...
def some_business_logic(self):
# 单例的业务逻辑方法
pass
# 使用示例
s1 = SingletonImproved()
s2 = SingletonImproved()
print(s1 is s2) # 输出: True
_instance = None
来存储实例,这样更加明确和易读。is None
检查而不是 hasattr
,这样更加直观和高效。__init__
方法来处理初始化逻辑。__init__
中使用一个标志来确保初始化代码只运行一次。对于需要参数化的单例,可以实现一个带参数的 __new__
方法:
class ParameterizedSingleton:
_instances = {}
def __new__(cls, *args, **kwargs):
# 将位置参数和关键字参数组合成一个不可变的 key,用作实例的唯一标识
key = (args, frozenset(kwargs.items()))
# 检查是否已经存在对应的实例
if key not in cls._instances:
cls._instances[key] = super().__new__(cls)
return cls._instances[key]
def __init__(self, *args, **kwargs):
# 确保 __init__ 只被调用一次
if not hasattr(self, 'initialized'):
self.args = args
self.kwargs = kwargs
self.initialized = True
print(f"Initializing with args: {args} and kwargs: {kwargs}")
def get_parameters(self):
return self.args, self.kwargs
# 创建第一个实例
instance1 = ParameterizedSingleton(10, name="test1")
print(instance1.get_parameters()) # 输出: ((10,), {'name': 'test1'})
# 创建第二个实例,参数不同
instance2 = ParameterizedSingleton(20, name="test2")
print(instance2.get_parameters()) # 输出: ((20,), {'name': 'test2'})
# 创建与第一个实例相同参数的实例
instance3 = ParameterizedSingleton(10, name="test1")
print(instance3.get_parameters()) # 输出: ((10,), {'name': 'test1'})
# 检查单例特性
print(instance1 is instance3) # 输出: True
print(instance1 is instance2) # 输出: False
这个版本:
上面的代码都不是线程安全的:
# 线程不安全的单例实现
class ThreadUnsafeSingleton:
_instance = None
def __new__(cls):
if cls._instance is None:
time.sleep(0.1) # 模拟耗时操作
cls._instance = super().__new__(cls)
return cls._instance
线程不安全的原因:
if cls._instance is None
这一行。_instance
为 None,它们都会尝试创建新实例。线程不安全将导致的问题:
这里就用锁来改进:
import threading
import time
# 线程不安全的单例实现
class ThreadUnsafeSingleton:
_instance = None
def __new__(cls):
if cls._instance is None:
time.sleep(0.1) # 模拟耗时操作
cls._instance = super().__new__(cls)
return cls._instance
# 线程安全的单例实现
import threading
class ThreadSafeSingleton:
_instance = None
_lock = threading.Lock()
def __new__(cls):
if cls._instance is None:
with cls._lock:
if cls._instance is None:
time.sleep(0.1) # 模拟耗时操作
cls._instance = super().__new__(cls)
return cls._instance
# 测试函数
def test_singleton(Singleton):
def create_singleton():
singleton = Singleton()
print(f"Instance created: {id(singleton)}")
threads = [threading.Thread(target=create_singleton) for _ in range(10)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
print("Testing ThreadUnsafeSingleton:")
test_singleton(ThreadUnsafeSingleton)
print("\nTesting ThreadSafeSingleton:")
test_singleton(ThreadSafeSingleton)
双重检查锁定的工作原理:
- 第一次检查不使用锁,以避免每次获取实例都需要加锁。
- 如果第一次检查发现实例不存在,才使用锁。
- 加锁后再次检查,以确保在等待锁的过程中没有其他线程创建实例。
在线程安全的版本上实现带参数的 __new__
方法,这里结合了参数化单例和线程安全性,非常适用于需要基于不同参数创建不同单例实例,同时又需要确保线程安全的场景。
import threading
import concurrent.futures
class ThreadSafeParameterizedSingleton:
_instances = {}
_lock = threading.Lock()
def __new__(cls, parameter):
if parameter not in cls._instances:
with cls._lock:
# 双重检查锁定
if parameter not in cls._instances:
cls._instances[parameter] = super().__new__(cls)
return cls._instances[parameter]
def __init__(self, parameter):
# 确保 __init__ 只被调用一次
if not hasattr(self, 'initialized'):
with self.__class__._lock:
if not hasattr(self, 'initialized'):
self.parameter = parameter
self.initialized = True
print(f"Initializing with parameter: {parameter}")
def get_parameter(self):
return self.parameter
# 测试函数
def test_singleton(parameter):
instance = ThreadSafeParameterizedSingleton(parameter)
print(f"Instance for {parameter}: {id(instance)}")
return instance
# 多线程测试
parameters = ["A", "B", "A", "C", "B", "A"]
with concurrent.futures.ThreadPoolExecutor(max_workers=len(parameters)) as executor:
futures = [executor.submit(test_singleton, param) for param in parameters]
instances = [future.result() for future in concurrent.futures.as_completed(futures)]
# 验证结果
for param, instance in zip(parameters, instances):
print(f"Parameter: {param}, Instance ID: {id(instance)}, Value: {instance.get_parameter()}")
# 检查 "A" 参数的所有实例是否相同
a_instances = [instance for instance in instances if instance.get_parameter() == "A"]
print(f"\nAll 'A' instances are the same: {len(set(a_instances)) == 1}")
使用注意事项:
最直接的方法:
class Singleton {
constructor() {
if (Singleton.instance) {
return Singleton.instance;
}
this.value = Math.random(); // 模拟一些独特的实例状态
Singleton.instance = this;
}
getValue() {
return this.value;
}
}
// 测试单例模式
const instance1 = new Singleton();
const instance2 = new Singleton();
console.log(instance1 === instance2); // 输出: true
console.log(instance1.value);
console.log(instance2.value);
console.log(instance1.getValue() === instance2.getValue()); // 输出: true
虽然上面的代码实现了单例模式,但有一些改进可以让它更健壮、更灵活。以下是一些改进建议:
class Singleton {
constructor() {
if (Singleton.instance) {
return Singleton.instance;
}
this.value = Math.random(); // 模拟实例状态
Singleton.instance = this;
// 冻结实例,防止修改
Object.freeze(Singleton.instance);
}
getValue() {
return this.value;
}
static getInstance() {
// 如果实例不存在,创建一个新实例
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
}
// 测试单例模式
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // 输出: true
console.log(instance1.getValue() === instance2.getValue()); // 输出: true
// 测试冻结的实例
instance1.value = 42;
console.log(instance1.getValue()); // 输出依然是之前的随机数,未修改为 42
解释:
// 导出
export default Singleton;
// 在其他模块中可以使用:
import Singleton from './Singleton';
const instance = Singleton.getInstance();
为了实现一个能够在惰性初始化时接受任意参数的单例模式,我们需要允许 getInstance() 方法在实例化时接收参数,并将这些参数传递给构造函数。
class Singleton {
constructor(...args) {
if (Singleton.instance) {
return Singleton.instance;
}
// 保存传入的参数
this.init(...args);
Singleton.instance = this;
// 冻结实例,防止修改
Object.freeze(Singleton.instance);
}
// 初始化方法,保存构造函数参数
init(...args) {
this.params = args;
}
getParameters() {
return this.params;
}
static getInstance(...args) {
// 如果实例不存在,创建一个新实例,并传入参数
if (!Singleton.instance) {
Singleton.instance = new Singleton(...args);
}
return Singleton.instance;
}
}
// 测试单例模式
const instance1 = Singleton.getInstance(10, "Hello", { key: "value" });
console.log(instance1.getParameters()); // 输出: [10, "Hello", { key: "value" }]
const instance2 = Singleton.getInstance(20, "World");
console.log(instance2.getParameters()); // 输出依然是: [10, "Hello", { key: "value" }]
// 检查单例特性
console.log(instance1 === instance2); // 输出: true
数据库连接池(Database Connection Pool)是管理数据库连接的一种技术,旨在提高数据库访问的效率和性能。它通过维护一个连接池,池中包含一组已经建立的数据库连接,供应用程序重复使用,而不是每次需要数据库连接时都创建一个新的连接。
- 连接池初始化:应用程序启动时,连接池会创建并维护一定数量的数据库连接。
- 获取连接:当应用程序需要访问数据库时,它从连接池中请求一个空闲的连接。
- 使用连接:应用程序使用这个连接进行数据库操作。
- 释放连接:操作完成后,应用程序将连接返回到连接池,而不是关闭它。
- 重复使用:连接池中的连接可以被多个请求重复使用,避免了频繁的连接创建和销毁。
将以一个数据库连接池管理器为例,展示单例模式的一个常见应用场景。
import random
import threading
import time
class DatabaseConnectionPool:
_instances = {}
_lock = threading.Lock()
def __new__(cls, db_name):
if db_name not in cls._instances:
with cls._lock:
if db_name not in cls._instances:
cls._instances[db_name] = super().__new__(cls)
return cls._instances[db_name]
def __init__(self, db_name):
if not hasattr(self, 'initialized'):
with self.__class__._lock:
if not hasattr(self, 'initialized'):
self.db_name = db_name
self.connections = []
self.max_connections = 5
self.initialized = True
print(f"初始化 {db_name} 数据库的连接池")
def get_connection(self):
if not self.connections:
new_connection = self._create_connection()
print(f"为 {self.db_name} 创建了新连接")
return new_connection
return self.connections.pop()
def release_connection(self, connection):
if len(self.connections) < self.max_connections:
self.connections.append(connection)
else:
del connection
def _create_connection(self):
# 模拟创建数据库连接
time.sleep(0.1) # 假设连接需要一些时间
return f"到 {self.db_name} 的连接"
def worker(db_name):
pool = DatabaseConnectionPool(db_name)
connection = pool.get_connection()
print(f"线程 {threading.current_thread().name} 获得了 {connection}")
# 模拟使用连接
time.sleep(random.uniform(0.1, 0.3))
pool.release_connection(connection)
# 测试
databases = ["MySQL", "PostgreSQL", "MySQL", "MongoDB", "PostgreSQL"]
threads = []
for db in databases:
thread = threading.Thread(target=worker, args=(db,))
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
# 验证结果
print("\n连接池实例验证:")
for db_name, instance in DatabaseConnectionPool._instances.items():
print(f"数据库: {db_name}, 连接池ID: {id(instance)}")
1,单例模式的应用:
2,参数化的必要性:
3,线程安全的重要性:
4,连接池的实现:
5,实际应用模拟:
6,验证:
可改进:
在实际的应用中,单例模式常用于全局管理配置数据。配置管理器需要在整个应用中只有一个实例,以确保配置的一致性和正确性。任何时候访问配置管理器,都应返回相同的实例,这样可以避免数据冲突,并且所有组件都能共享相同的配置。
class ConfigManager {
constructor(initialConfig = {}) {
if (ConfigManager.instance) {
return ConfigManager.instance;
}
// 初始化配置
this.config = initialConfig;
// 保存实例
ConfigManager.instance = this;
// 冻结实例,防止修改
Object.freeze(ConfigManager.instance);
}
// 获取配置
getConfig(key) {
return this.config[key];
}
// 设置配置
setConfig(key, value) {
this.config[key] = value;
}
// 获取所有配置
getAllConfig() {
return this.config;
}
// 静态方法:获取单例实例
static getInstance(initialConfig = {}) {
if (!ConfigManager.instance) {
ConfigManager.instance = new ConfigManager(initialConfig);
}
return ConfigManager.instance;
}
}
// 测试代码
// 第一次获取单例实例,并设置初始配置
const config1 = ConfigManager.getInstance({ appName: "MyApp", version: "1.0" });
console.log(config1.getAllConfig()); // 输出: { appName: "MyApp", version: "1.0" }
// 修改配置
config1.setConfig("appName", "YourApp");
console.log(config1.getConfig("appName")); // 输出: "YourApp"
// 再次获取单例实例
const config2 = ConfigManager.getInstance();
console.log(config2.getAllConfig()); // 输出: { appName: "YourApp", version: "1.0" }
// 检查单例特性
console.log(config1 === config2); // 输出: true
Pinia 是 Vue 生态中的一种状态管理库,是 Vuex 的替代方案。尽管 Pinia 并不直接被称为“单例模式”,但其工作方式具有类似的单例模式特性,特别是在全局状态管理的场景中。
Pinia 是用于管理 Vue 应用程序全局状态的状态管理库。以下是 Pinia 的工作机制:
要模拟实现类似 Pinia 的全局状态管理系统,我们可以通过 JavaScript 创建一个简单的状态管理库,该库允许你定义 store 并在全局共享状态。以下是一个简化版的实现,包括state、actions、getter 和持久化支持。
// 全局存储所有 store 的实例
const stores = {};
function defineStore(id, options) {
if (stores[id]) {
return stores[id]; // 如果 store 已存在,直接返回
}
// 创建 store 的响应式状态
const state = options.state ? options.state() : {};
// 定义 store
const store = {
state, // 保存状态
actions: {}, // 保存动作
getters: {}, // 保存 getter
};
// 处理 actions
if (options.actions) {
for (const [key, action] of Object.entries(options.actions)) {
store.actions[key] = action.bind(store); // 绑定 store 上下文
}
}
// 处理 getters
if (options.getters) {
for (const [key, getter] of Object.entries(options.getters)) {
Object.defineProperty(store.getters, key, {
get: () => getter(store.state), // getter 通过计算属性返回值
});
}
}
// 将 store 保存在全局 stores 对象中
stores[id] = store;
return store;
}
// 示例用法
const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
}),
actions: {
increment() {
this.state.count++;
},
reset() {
this.state.count = 0;
},
},
getters: {
doubleCount(state) {
return state.count * 2;
},
},
});
// 在不同的地方使用 store
const counterStore1 = useCounterStore;
const counterStore2 = useCounterStore;
counterStore1.actions.increment();
console.log(counterStore1.state.count); // 输出: 1
console.log(counterStore2.getters.doubleCount); // 输出: 2
counterStore2.actions.reset();
console.log(counterStore1.state.count); // 输出: 0
如果使用 Vue 3,我们可以使用 reactive 或 ref 来替换普通的 state,使其具有响应性:
import { reactive } from 'vue';
// 在 defineStore 中替换 state 的初始化方式
const state = reactive(options.state ? options.state() : {});