设计模式-单例模式
单例模式我们日常开发中应该使用的,非常多,而且在很多第三方框架中可以看到它的身影。那么今天就让我们来学习单例模式吧。
一、什么是单例模式?
1.1 介绍
单例模式是一种创建型设计模式, 能够保证一个类只有一个实例,并提供一个访问它的全局访问点。
该模式有三个基本要点:
1.该类只能有一个实例;
2.该类必须自行创建这个实例;
3.该类必须自行向整个系统提供这个实例。
我们知道普通构造函数无法实现上述行为, 因为构造函数的设计决定了它必须总是返回一个新对象。
那么为什么会出现有人想要控制一个类所拥有的实例数量?
最常见的原因是控制某些共享资源 (例如数据库或文件) 的访问权限。
由于在一个系统中,一个类经常会被使用在不同的地方,通过单例模式,我们可以避免多次创建多个实例,从而节约系统资源。
1.2 实现方式
单例模式有两种实现方式为"饿汉模式"和"懒汉模式":
1. 饿汉模式
在类初始化阶段就已经在堆内存中开辟了一块内存,用于存放实例化对象,所以也称为饿汉模式。饿汉模式实现的单例的优点是,可以保证多线程情况下实例的唯一性,而且 getInstance 直接返回唯一实例,性能非常高。如果类成员变量比较多,或变量比较大的情况下,这种模式可能会在没有使用类对象的情况下,一直占用堆内存,如果第三方框架都采用这种方式对系统来说是灾难性的。
2.懒汉模式
懒汉模式其实就是为了解决饿汉,类一加载就创建对象的问题。懒汉加载就是在系统使用到该类对象的时候,才会将实例加载到堆内存中。
二、优缺点
2.1 优点
-
可以保证一个类只有一个实例。
-
获得了一个指向该实例的全局访问节点。
-
首次请求单例对象时对其进行初始化。
2.2 缺点
-
违反了单一职责原则。 该模式同时解决了两个问题。
-
单例模式可能掩盖不良设计, 比如程序各组件之间相互了解过多等。
-
该模式在多线程环境下需要进行特殊处理, 避免多个线程多次创建单例对象。
-
单例的客户端代码单元测试可能会比较困难, 因为许多测试框架以基于继承的方式创建模拟对象。 由于单例类的构造函数是私有的, 而且绝大部分语言无法重写静态方法, 所以你需要想出仔细考虑模拟单例的方法。 要么干脆不编写测试代码, 或者不使用单例模式。
三、解决方案
所有单例的实现都包含以下两个相同的步骤:
1.将默认构造函数设为私有, 防止其他对象使用单例类的 new运算符,尽可能避免出现多实例,防止违背基本点1.
2.新建一个静态构建方法作为构造函数。 该函数会 “偷偷” 调用私有构造函数来创建对象, 并将其保存在一个静态成员变量中。 此后所有对于该函数的调用都将返回这一缓存对象。
如果你的代码能够访问单例类, 那它就能调用单例类的静态方法。 无论何时调用该方法, 它总是会返回相同的对象。
四、适用场景
-
如果程序中的某个类对于所有客户端只有一个可用的实例, 可以使用单例模式。
单例模式禁止通过除特殊构建方法以外的任何方式来创建自身类的对象。 该方法可以创建一个新对象, 但如果该对象已经被创建, 则返回已有的对象。
-
如果你需要更加严格地控制全局变量, 可以使用单例模式。 单例模式与全局变量不同, 它保证类只存在一个实例。 除了单例类自己以外, 无法通过任何方式替换缓存的实例。
五、代码实现
5.1 懒汉模式-代码实现(多线程不安全)
//懒汉模式
public final class Singleton {
private static Singleton instance= null;
//构造函数
private Singleton(){}
//通过该函数向整个系统提供实例
public static Singleton getInstance(){
//当instance为null时,则实例化对象,否则直接返回对象
if(null == instance){
instance = new Singleton();
}
return instance;
}
}
5.2 懒汉模式-代码实现(内部类)
//懒汉模式 内部类实现
public final class Singleton {
private Singleton() {
}
// 内部类实现
public static class InnerSingleton {
private static Singleton instance = new Singleton();
}
// 返回内部类中的静态变量
public static Singleton getInstance() {
return InnerSingleton.instance;
}
}
5.3 懒汉模式-代码实现(Double check)
//懒汉模式 + synchronized同步锁 + double-check
public final class Singleton {
private volatile static Singleton instance;
private Singleton(){
}
public static Singleton getInstance(){
//第一次判断,当instance为null时,则实例化对象,否则直接返回对象
if(null == instance){
synchronized (Singleton.class){
//第二次判断
if(null == instance){
instance = new Singleton();
}
}
}
return instance;
}
}
六、参考引用
-
《Design Patterns》 亚历山大·什韦茨(Alexander Shvets)深入理解设计模式 一书