设计模式:单例模式

意图

单例模式(Singleton)是一种创建型设计模式让你能够保证一个类只有一个实例并提供一个访问该实例的全局节点

解决方案

所有单例的实现都包含以下两个相同的步骤

  • 将默认构造函数设为私有防止其他对象使用单例类的 new运算符
  • 新建一个静态构建方法作为构造函数该函数会 偷偷 调用私有构造函数来创建对象并将其保存在一个静态成员变量中此后所有对于该函数的调用都将返回这一缓存对象

如果你的代码能够访问单例类那它就能调用单例类的静态方法无论何时调用该方法它总是会返回相同的对象

单例模式结构

单例模式结构

单例Singleton类声明了一个名为 get­Instance获取实例的静态方法来返回其所属类的一个相同实例

单例的构造函数必须对客户端Client代码隐藏调用 获取实例方法必须是获取单例对象的唯一方式

单例模式适合应用场景

  • 如果程序中的某个类对于所有客户端只有一个可用的实例可以使用单例模式

单例模式禁止通过除特殊构建方法以外的任何方式来创建自身类的对象该方法可以创建一个新对象但如果该对象已经被创建则返回已有的对象

  • 如果你需要更加严格地控制全局变量可以使用单例模式

单例模式与全局变量不同它保证类只存在一个实例除了单例类自己以外无法通过任何方式替换缓存的实例

请注意 你可以随时调整限制并设定生成单例实例的数量 只需修改 获取实例方法 即 getInstance 中的代码即可实现

单例模式优缺点

优点:

  • 你可以保证一个类只有一个实例
  • 你获得了一个指向该实例的全局访问节点
  • 仅在首次请求单例对象时对其进行初始化

缺点:

  • 违反了_单一职责原则_该模式同时解决了两个问题
  • 单例模式可能掩盖不良设计比如程序各组件之间相互了解过多等
  • 该模式在多线程环境下需要进行特殊处理避免多个线程多次创建单例对象
  • 单例的客户端代码单元测试可能会比较困难因为许多测试框架以基于继承的方式创建模拟对象由于单例类的构造函数是私有的而且绝大部分语言无法重写静态方法所以你需要想出仔细考虑模拟单例的方法要么干脆不编写测试代码或者不使用单例模式

与其他模式的关系

  • 门面模式类通常可以转换为单例模式 因为在大部分情况下一个外观对象就足够了

  • 如果你能将对象的所有共享状态简化为一个享元对象 那么享元模式就和单例类似了 但这两个模式有两个根本性的不同

    1. 只会有一个单例实体 但是享元类可以有多个实体 各实体的内在状态也可以不同
    2. 单例对象可以是可变的 享元对象是不可变的
  • 抽象工厂模式 生成器模式原型模式都可以用单例来实现 

在 Java 中使用模式

使用示例许多开发者将单例模式视为一种反模式因此它在 Java 代码中的使用频率正在逐步减少

尽管如此Java 核心程序库中仍有相当多的单例示例

 示例代码

public class SingletonObject implements Serializable {
    private static final SingletonObject INSTANCE = new SingletonObject();

    private SingletonObject() {
        if (INSTANCE != null) {
            throw new IllegalStateException("Singleton");
        }
    }

    public static SingletonObject getInstance() {
        return INSTANCE;
    }

    private Object readResolve() {
        return INSTANCE;
    }
}

延迟加载

public class Singleton {
  private Singleton() {
  }

  public static Singleton getInstance() {
    return Holder.INSTANCE;
  }

  private static class Holder {
    private static final Singleton INSTANCE = new Singleton();
  }
}

 枚举

public enum Singleton {
    INSTANCE;

    public String hello(String name) {
        return new Date() + ": hello " + name + "!";
    }
}