您现在的位置是:网站首页 > 多例模式(Multiton)的特殊应用场景文章详情
多例模式(Multiton)的特殊应用场景
陈川
【
JavaScript
】
1796人已围观
9057字
多例模式(Multiton)的特殊应用场景
多例模式是单例模式的扩展,它允许一个类有多个实例,但每个实例都与一个唯一的键相关联。与单例模式全局唯一实例不同,多例模式通过键来管理多个实例,确保相同的键总是返回相同的实例。这种模式在需要按特定条件创建和管理多个单例时特别有用。
多例模式的基本实现
在JavaScript中,多例模式可以通过一个对象来存储实例,使用键作为属性名。下面是一个基本实现:
class Multiton {
static instances = {};
constructor(key) {
if (Multiton.instances[key]) {
return Multiton.instances[key];
}
this.key = key;
// 其他初始化代码
Multiton.instances[key] = this;
}
static getInstance(key) {
if (!Multiton.instances[key]) {
Multiton.instances[key] = new Multiton(key);
}
return Multiton.instances[key];
}
}
// 使用示例
const instance1 = Multiton.getInstance('key1');
const instance2 = Multiton.getInstance('key1');
console.log(instance1 === instance2); // true
const instance3 = Multiton.getInstance('key2');
console.log(instance1 === instance3); // false
配置管理的多例应用
在大型应用中,不同模块可能需要不同的配置实例。使用多例模式可以确保每个模块获取到自己的配置实例,同时避免重复创建:
class AppConfig {
static configs = {};
constructor(moduleName) {
if (AppConfig.configs[moduleName]) {
return AppConfig.configs[moduleName];
}
this.moduleName = moduleName;
this.loadConfig();
AppConfig.configs[moduleName] = this;
}
loadConfig() {
// 模拟从服务器加载配置
console.log(`Loading config for ${this.moduleName}`);
this.settings = {
apiUrl: `https://api.example.com/${this.moduleName}`,
timeout: 5000
};
}
static getConfig(moduleName) {
if (!AppConfig.configs[moduleName]) {
AppConfig.configs[moduleName] = new AppConfig(moduleName);
}
return AppConfig.configs[moduleName];
}
}
// 使用示例
const userConfig = AppConfig.getConfig('user');
const productConfig = AppConfig.getConfig('product');
console.log(userConfig.settings.apiUrl); // https://api.example.com/user
console.log(productConfig.settings.apiUrl); // https://api.example.com/product
多语言支持中的多例模式
在多语言应用中,每种语言资源可以作为一个多例实例管理:
class I18n {
static instances = {};
constructor(locale) {
if (I18n.instances[locale]) {
return I18n.instances[locale];
}
this.locale = locale;
this.translations = this.loadTranslations();
I18n.instances[locale] = this;
}
loadTranslations() {
// 模拟加载翻译资源
const translations = {
'en-US': {
greeting: 'Hello',
farewell: 'Goodbye'
},
'zh-CN': {
greeting: '你好',
farewell: '再见'
}
};
return translations[this.locale] || translations['en-US'];
}
t(key) {
return this.translations[key] || key;
}
static getInstance(locale) {
if (!I18n.instances[locale]) {
I18n.instances[locale] = new I18n(locale);
}
return I18n.instances[locale];
}
}
// 使用示例
const enI18n = I18n.getInstance('en-US');
const zhI18n = I18n.getInstance('zh-CN');
console.log(enI18n.t('greeting')); // Hello
console.log(zhI18n.t('greeting')); // 你好
连接池管理的多例应用
在数据库或网络连接管理中,多例模式可以用于管理不同类型的连接池:
class ConnectionPool {
static pools = {};
constructor(type) {
if (ConnectionPool.pools[type]) {
return ConnectionPool.pools[type];
}
this.type = type;
this.connections = [];
this.maxSize = 10;
ConnectionPool.pools[type] = this;
}
getConnection() {
if (this.connections.length < this.maxSize) {
const conn = this.createConnection();
this.connections.push(conn);
return conn;
}
throw new Error('Connection pool exhausted');
}
createConnection() {
// 模拟创建连接
return {
id: Math.random().toString(36).substr(2, 9),
type: this.type,
connected: true
};
}
static getPool(type) {
if (!ConnectionPool.pools[type]) {
ConnectionPool.pools[type] = new ConnectionPool(type);
}
return ConnectionPool.pools[type];
}
}
// 使用示例
const mysqlPool = ConnectionPool.getPool('mysql');
const redisPool = ConnectionPool.getPool('redis');
const conn1 = mysqlPool.getConnection();
const conn2 = redisPool.getConnection();
console.log(conn1.type); // mysql
console.log(conn2.type); // redis
主题管理的多例实现
在UI主题系统中,多例模式可以管理不同的主题实例:
class Theme {
static themes = {};
constructor(name) {
if (Theme.themes[name]) {
return Theme.themes[name];
}
this.name = name;
this.styles = this.loadStyles();
Theme.themes[name] = this;
}
loadStyles() {
const styles = {
light: {
primaryColor: '#ffffff',
secondaryColor: '#f0f0f0',
textColor: '#333333'
},
dark: {
primaryColor: '#222222',
secondaryColor: '#333333',
textColor: '#ffffff'
},
blue: {
primaryColor: '#1e88e5',
secondaryColor: '#64b5f6',
textColor: '#ffffff'
}
};
return styles[this.name] || styles.light;
}
apply() {
console.log(`Applying ${this.name} theme`);
// 实际应用中会更新CSS变量或直接修改DOM样式
document.documentElement.style.setProperty('--primary', this.styles.primaryColor);
document.documentElement.style.setProperty('--secondary', this.styles.secondaryColor);
document.documentElement.style.setProperty('--text', this.styles.textColor);
}
static getTheme(name) {
if (!Theme.themes[name]) {
Theme.themes[name] = new Theme(name);
}
return Theme.themes[name];
}
}
// 使用示例
const lightTheme = Theme.getTheme('light');
const darkTheme = Theme.getTheme('dark');
lightTheme.apply(); // 应用浅色主题
缓存管理的多例应用
在多级缓存系统中,可以为不同类型的缓存数据创建不同的多例实例:
class CacheManager {
static caches = {};
constructor(cacheType) {
if (CacheManager.caches[cacheType]) {
return CacheManager.caches[cacheType];
}
this.cacheType = cacheType;
this.data = new Map();
this.maxSize = 100;
CacheManager.caches[cacheType] = this;
}
set(key, value) {
if (this.data.size >= this.maxSize) {
const firstKey = this.data.keys().next().value;
this.data.delete(firstKey);
}
this.data.set(key, value);
}
get(key) {
return this.data.get(key);
}
static getCache(cacheType) {
if (!CacheManager.caches[cacheType]) {
CacheManager.caches[cacheType] = new CacheManager(cacheType);
}
return CacheManager.caches[cacheType];
}
}
// 使用示例
const userCache = CacheManager.getCache('user');
const productCache = CacheManager.getCache('product');
userCache.set('user1', {name: 'Alice', age: 25});
productCache.set('product1', {name: 'Laptop', price: 999});
console.log(userCache.get('user1')); // {name: 'Alice', age: 25}
console.log(productCache.get('product1')); // {name: 'Laptop', price: 999}
多例模式的性能考虑
虽然多例模式提供了便利的实例管理,但也需要注意一些性能问题:
- 内存泄漏风险:多例实例会一直存在于内存中,如果键是动态生成的且数量不受限制,可能导致内存泄漏。解决方案是设置最大实例数或实现清理机制。
class SafeMultiton {
static instances = new Map();
static maxInstances = 100;
static instanceKeys = [];
constructor(key) {
if (SafeMultiton.instances.has(key)) {
return SafeMultiton.instances.get(key);
}
// 清理最旧的实例如果达到上限
if (SafeMultiton.instanceKeys.length >= SafeMultiton.maxInstances) {
const oldestKey = SafeMultiton.instanceKeys.shift();
SafeMultiton.instances.delete(oldestKey);
}
this.key = key;
SafeMultiton.instances.set(key, this);
SafeMultiton.instanceKeys.push(key);
}
static getInstance(key) {
if (!SafeMultiton.instances.has(key)) {
SafeMultiton.instances.set(key, new SafeMultiton(key));
}
return SafeMultiton.instances.get(key);
}
}
-
线程安全问题:在JavaScript的单线程环境中这不是问题,但在Web Worker等多线程环境下需要考虑同步问题。
-
测试困难:多例实例在测试间共享状态,可能导致测试相互影响。解决方案是在测试前后重置多例存储:
// 测试示例
describe('Multiton', () => {
beforeEach(() => {
// 清空实例存储
Multiton.instances = {};
});
it('should return same instance for same key', () => {
const instance1 = Multiton.getInstance('test');
const instance2 = Multiton.getInstance('test');
expect(instance1).toBe(instance2);
});
});
多例模式与依赖注入的结合
在多例模式中结合依赖注入可以创建更灵活的架构:
class ServiceLocator {
static services = {};
static register(name, creator) {
ServiceLocator.services[name] = {
creator,
instance: null
};
}
static get(name) {
const service = ServiceLocator.services[name];
if (!service) {
throw new Error(`Service ${name} not registered`);
}
if (!service.instance) {
service.instance = service.creator();
}
return service.instance;
}
static reset() {
for (const name in ServiceLocator.services) {
ServiceLocator.services[name].instance = null;
}
}
}
// 注册服务
ServiceLocator.register('logger', () => ({
log: (message) => console.log(`[LOG] ${message}`),
error: (message) => console.error(`[ERROR] ${message}`)
}));
ServiceLocator.register('api', () => ({
fetchData: () => Promise.resolve({data: 'sample data'})
}));
// 使用服务
const logger = ServiceLocator.get('logger');
const api = ServiceLocator.get('api');
logger.log('Fetching data...');
api.fetchData().then(data => logger.log(`Data received: ${data.data}`));