如何结合用户提供的工厂方法实现Multiton(多例)模式?
如何结合用户提供的工厂方法实现Multiton(多例)模式?
嘿,我来帮你搞定Rust里这个结合用户工厂方法的多例模式实现,都是实打实的Rust风格写法,咱们一步步来!
首先得明确咱们要达成的核心目标:
- 给定一个字符串键,能直接返回已经存在的对应实例
- 要是这个键还没创建过实例,就调用用户提供的工厂方法生成新实例,存进管理器后再返回
- 必须保证线程安全,毕竟实际项目里多线程并发访问是常有的事,总不能因为并发创建搞出多个相同键的实例对吧?
第一步:定义基础Trait
先把你提到的IFoo trait落地,用户的自定义类型必须实现这个trait,才能被多例管理器接管,我给加个简单方法方便测试:
pub trait IFoo { fn do_something(&self) -> String; }
第二步:实现多例管理器(Multiton)
核心得用线程安全的容器来存实例,Rust里咱们用Arc<Mutex<HashMap<String, Arc<dyn IFoo>>>>这个组合:
Arc:让管理器和实例都能被多个线程共享,靠引用计数保证安全释放Mutex:保护内部的HashMap,防止多个线程同时修改导致数据竞争HashMap:用字符串键映射到对应实例,实例用Arc<dyn IFoo>包裹,方便多场景共享
先写管理器结构体,还可以整个全局实例方便用户直接用:
use std::collections::HashMap; use std::sync::{Arc, Mutex}; use lazy_static::lazy_static; pub struct Multiton { instances: Mutex<HashMap<String, Arc<dyn IFoo>>>, } // 给管理器加默认初始化方法 impl Default for Multiton { fn default() -> Self { Multiton { instances: Mutex::new(HashMap::new()), } } } // 全局多例管理器,不用用户每次自己实例化 lazy_static! { pub static ref GLOBAL_MULTITON: Arc<Multiton> = Arc::new(Multiton::default()); }
要是用户需要分开管理多组独立的多例,不用全局实例,直接创建Multiton实例就行,完全独立互不干扰。
第三步:实现核心的get_or_create方法
这个方法就是咱们的核心逻辑,接收键和用户的工厂闭包,处理实例的获取或创建:
impl Multiton { pub fn get_or_create<F, T>(&self, key: &str, factory: F) -> Arc<dyn IFoo> where F: FnOnce() -> T, T: IFoo + 'static, { // 先获取锁,这里用unwrap是简化处理,实际项目可以用expect或返回Result let mut instances = self.instances.lock().unwrap(); // 检查有没有现成实例,有就直接返回克隆的引用 if let Some(instance) = instances.get(key) { return Arc::clone(instance); } // 没有的话调用工厂方法创建新实例,包装成Arc let new_instance = Arc::new(factory()); // 存进HashMap instances.insert(key.to_string(), Arc::clone(&new_instance)); // 返回新实例 new_instance } }
这里的泛型约束F: FnOnce() -> T是说工厂闭包只需要被调用一次,T: IFoo + 'static是因为要把T转换成dyn IFoo trait object,必须保证T的生命周期是静态的,不然没法安全存进管理器。
第四步:用户侧的使用示例
用户只需要定义自己的类型实现IFoo,然后调用get_or_create就行:
// 用户自定义类型,实现IFoo trait struct MyFoo { name: String, } impl IFoo for MyFoo { fn do_something(&self) -> String { format!("Hello from MyFoo: {}", self.name) } } fn main() { // 第一次获取"foo1",会触发工厂方法创建实例 let instance1 = GLOBAL_MULTITON.get_or_create("foo1", || { println!("Creating foo1 instance!"); MyFoo { name: "foo1".to_string() } }); println!("{}", instance1.do_something()); // 输出:Hello from MyFoo: foo1 // 再次获取"foo1",不会触发创建,直接返回已有实例 let instance2 = GLOBAL_MULTITON.get_or_create("foo1", || { println!("Creating foo1 instance again!"); // 这句不会打印 MyFoo { name: "foo1".to_string() } }); println!("{}", instance2.do_something()); // 输出和上面一样 // 新键"foo2",触发创建 let instance3 = GLOBAL_MULTITON.get_or_create("foo2", || { println!("Creating foo2 instance!"); MyFoo { name: "foo2".to_string() } }); println!("{}", instance3.do_something()); // 输出:Hello from MyFoo: foo2 }
一些实用小提示
- 错误处理:示例里用
unwrap()是简化写法,实际项目里建议换成expect("锁获取失败")或者返回Result类型,避免panic拖垮程序 - 自定义多例组:要是用户需要多组独立的多例,直接创建
let my_multiton = Multiton::default();就行,和全局实例完全隔离 - 性能优化:如果你的场景并发量特别大,也可以把
Mutex换成RwLock,读多写少的场景下性能会更好
内容来源于stack exchange
相关产品推荐
相关产品推荐

