开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第32天,点击查看活动详情
单例模式是一种常见的设计模式,它保证一个类只有一个实例,并提供一个全局访问点来访问这个实例。这样做的目的是为了限制实例的数量,并节省系统资源。
实现单例模式的方法有很多种,但常见的有以下几种:
- 懒汉式单例:在第一次调用时才创建实例,但存在线程安全问题。
- 饿汉式单例:在类加载时就创建实例,避免了线程安全问题,但如果实例创建失败会导致类加载失败。
- 双重检查锁单例:使用双重检查锁的方式来保证线程安全,并且在第一次调用时才创建实例。
- 枚举单例:使用枚举类型来实现单例,无需考虑线程安全问题。
单例模式有几个关键点:
- 一个类只有一个实例
- 该实例必须自行创建
- 必须自行向整个系统提供这个实例
下面是一个使用Rust语言实现单例模式的例子:
use std::sync::{Once, ONCE_INIT};
use std::cell::RefCell;
struct Singleton;
impl Singleton {
fn new() -> Singleton {
Singleton
}
}
static mut SINGLETON: *const Singleton = 0 as *const Singleton;
static INIT: Once = ONCE_INIT;
fn get_instance() -> &'static Singleton {
unsafe {
INIT.call_once(|| {
SINGLETON = Box::into_raw(Box::new(Singleton::new()));
});
&*SINGLETON
}
}
fn main() {
let instance1 = get_instance();
let instance2 = get_instance();
assert_eq!(instance1 as *const Singleton, instance2 as *const Singleton);
}
在上面的例子中,我们使用了std::sync::Once来保证get_instance函数只会被调用一次。SINGLETON是一个指向Singleton类型的指针,用于保存唯一的实例。在get_instance函数中,我们调用了Once的call_once方法,并传入一个闭包作为参数。当call_once被调用的时候,传入的闭包会被执行一次。
注意,call_once的参数闭包是不能直接调用的,而是通过Once的内部机制来调用的。这样做的好处是保证了闭包只会被调用一次,从而避免了并发的问题。
在闭包中,我们使用了Singleton的私有构造函数来创建一个新的实例。然后将这个实例的地址赋值给SINGLETON。最后,我们将SINGLETON作为函数的返回值返回。
在上面的代码中,我们使用了Once和闭包来保证get_instance函数只会被调用一次,并且我们使用了一个全局的指针来保存唯一的实例。这样就可以保证每次调用get_instance函数,都会返回同一个实例的地址。
使用单例模式的优点包括:
- 以保证系统中只有一个实例,避免了对象的重复创建,节省了系统资源。
- 可以方便地访问它,可以使用指定的方法来访问它,它具有一定的控制作用。
单例模式的缺点也很明显:
由于单例模式中没有抽象层,因此单例类的扩展有很大的困难;单例类的职责过重,在一定程度上违背了“单一职责原则”;由于单例模式中没有抽象层,因此无法形成基于继承的等级结构。
单例模式的使用场景:
- 当系统中只需要一个全局对象,且该对象有着复杂的初始化过程,可以使用单例模式。
- 当系统中需要频繁地访问一个对象,且该对象有着复杂的初始化过程,可以使用单例模式。
- 当系统中只需要一个登录界面或者设置界面,且该界面需要访问全局数据,可以使用单例模式。