概念
一个类只有允许创建一个实例对象
为什么用单例
- 节省资源,对于一些频繁创建和销毁的对象使用单例更好。
- 处理资源访问冲突问题
- 表示全局唯一类,如果有些数据在系统中只需要一份,那就比较适合设计为单例,比如配置信息。
为什么不使用全局变量或者静态变量
单例模式支持懒加载,在使用的时候才会生成实例,更加节省资源
为什么要使用单例模式而不使用静态方法
静态方法和单例模式使用的场景不同,如果只是一个简单过程方法,不存在状态,可以使用静态方法,比如生成随机数,生成唯一ID。
但一些情况下需要状态管理,比如下载功能,需要管理进程管理,下载进度管理。
实现
- 构造方法设为private,避免外部能直接通过构造函数生成对象
- 考虑线程安全问题
- 单例类不允许被继承(final)
//swift
final class PrintHelper{
static let `default` = PrintHelper()
private PrintHelper(){}
print(){}
}
实现方式分为两种:饿汉模式和懒汉模式
饿汉模式
加载就生成实例对象
优点:使用时不需要初始化对象,没有等待时间。
缺点:当对象大时,占用资源比较多。特别是在实例不是一开始就被使用。
//TypeScript
class PrintHelper{
static instance = new PrintHelper();
private constructor(){
console.log("constructor");
}
print(){
}
}
懒汉模式
使用时才加载。
优点:使用时才加载,不使用不占用资源
缺点:如果对象比较大,使用时初始化时间较长,等待时间比较长
//TypeScript
class PrintHelper{
private static _instance:PrintHelper
private constructor(){
console.log("constructor");
}
static getInstance():PrintHelper{
if(!PrintHelper._instance){
PrintHelper._instance = new PrintHelper()
}
return PrintHelper._instance
}
print(){
}
}
懒汉模式一定比饿汉模式好吗?
原则上是的,因为懒汉模式是用到才会对应的实例,不会在程序加载时就生成,这样节省内存。
如果生成对象耗时稍微长放在哪都会有较长时间,这时候不是考虑用懒汉模式还是饿汉模式,而是考虑怎么去优化。
单例的线程安全问题
在JavaScript(TS)中因为是单线程,不存在线程问题,所以可以不必考虑。但在支持多线程编程的语言中,要考虑线程安全问题。
务必保证在应用存活期间(一个进程中),只会生成一个实例对象。
单例模式的缺点
- 强耦合,直接在实例内部调用,隐藏了依赖关系
- 单例模式不利于使用多态、继承