【设计模式笔记17】:单例模式1-模式分析

文章目录

      • [六、 单例模式](#六、 单例模式)
        • [1. 模式动机](#1. 模式动机)
        • [2. 模式定义](#2. 模式定义)
        • [3. 结构及角色分析](#3. 结构及角色分析)
        • [4. 模式实现步骤分解](#4. 模式实现步骤分解)
        • [5. 完整代码示例与验证](#5. 完整代码示例与验证)

六、 单例模式

1. 模式动机
  • 模式名称: 单例模式 (Singleton)
  • 核心动机 : 在整个软件系统中,对于某些类来说,有且只能有一个实例。保证这个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。
  • 现实场景举例 :
    • 窗口管理器: 一个系统通常只有一个窗口管理器,负责管理所有窗口的层级、位置等。
    • 打印机假脱机服务: 系统中可以连接许多打印机,但只能有一个打印任务队列(假脱机服务)来统一管理和调度打印工作。
    • 配置文件读取类: 一个应用的配置信息通常是唯一的,用一个单例对象来读取和提供配置,可以避免重复读取和数据不一致。
  • 需要解决的问题: 我们怎样才能保证一个类只有一个实例,并且让系统中的任何部分都能方便地访问到这个唯一的实例呢?
  • 核心解决办法 : 让类自身负责保存它的唯一实例。这个类必须保证没有其它实例被创建,并且它可以提供一个方法来访问该实例。
2. 模式定义
  • 定义: 单例模式(Singleton Pattern)确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类被称为单例类,它提供全局访问的方法。
  • 三大要点 :
    1. 唯一实例: 某个类只能有一个实例。这是单例模式的根本。
    2. 自行创建: 它必须自行创建这个唯一的实例。创建的控制权在类的内部,而不是外部。
    3. 提供实例 : 它必须自行向整个系统提供这个实例。这意味着它需要提供一个全局访问点,并且在单例类的外部无法使用 new 关键字创建
  • 分类 : 单例模式是一种对象创建型模式
3. 结构及角色分析

单例模式的结构非常简单,只包含一个角色,即单例类 (Singleton)

图片描述 :一个名为Singleton的类图。其中包含:

  • 一个私有的、静态的、自身类型的属性 instance: Singleton
  • 一个私有的构造方法 Singleton()
  • 一个公有的、静态的、返回自身类型的方法 getInstance(): Singleton
    图中还有一个从类指向自身属性的菱形聚合符号,以及一个从 getInstance 方法指向 instance 属性的箭头,表示该方法会返回这个实例。
4. 模式实现步骤分解

为了实现单例模式的三个要点,我们需要一步步地构建这个类。

  • 第一步:私有化构造方法

    • 目的 : 防止外部通过 new 关键字随意创建实例。
    • 分析 : 要想在运行期间控制一个类的实例只有一个,首要任务就是要控制创建实例的地方。如果构造方法是公有的,那么在类的外部就可以随处 new,实例的数量就失控了。
    • 实现 : 将构造方法声明为 private
    java 复制代码
    private Singleton() {}
  • 第二步:提供获取实例的方法

    • 问题: 构造方法被私有化了,外部创建不了实例,也就无法调用对象的方法,这不行。
    • 解决: 单例类必须自己提供一个公有的方法,用来返回它内部创建好的那个唯一实例。
    java 复制代码
    // 初始设想,但有问题
    public Singleton getInstance() {}
  • 第三步:将获取实例的方法静态化

    • 问题: 上一步定义的是一个实例方法。要调用实例方法,必须先得有类的实例。这就陷入了一个"鸡生蛋,蛋生鸡"的死循环:为了获取实例,你得先有一个实例。
    • 解决 : 在方法上加上 static 关键字,使其成为一个类方法。这样就可以直接通过 类名.方法名() 的方式来调用,而不需要先得到类实例。
    java 复制代码
    public static Singleton getInstance() {}
  • 第四步:定义存储实例的静态属性

    • 问题 : getInstance() 方法的内部该如何实现?如果直接 return new Singleton();,那么每次调用都会创建一个新的实例,这违背了单例的初衷。
    • 解决: 在类内部用一个属性来记录(存储)已经创建好的那个唯一实例。当第一次创建后,就把实例保存在这个属性里,以后就直接复用这个实例,而不是重新创建。
    • 为什么要静态化 : 因为这个属性需要在静态方法 getInstance() 里面使用,而静态方法只能访问静态成员。所以这个存储实例的属性也必须是 static 的。
    java 复制代码
    private static Singleton instance = null;
  • 第五步:实现控制实例创建的逻辑

    • 目的 : 在 getInstance() 方法里实现对实例创建的控制。
    • 逻辑 :
      1. 先判断存储实例的静态属性 instance 是否有值(是否为 null)。
      2. 如果为 null,说明这是第一次调用,还没有创建过实例。此时,就 new 一个实例,并把它赋值给 instance 属性。
      3. 如果 instance 不为 null,说明之前已经创建过实例了。
      4. 最后,无论是否新建,都返回 instance 属性中存储的那个实例。
    java 复制代码
    public static Singleton getInstance() {
        // 先判断instance是否有值
        if (instance == null) {
            // 如果没有值,说明还没有创建过实例,那就创建一个
            // 并把这个实例设置给instance
            instance = new Singleton();
        }
        // 如果有值,或者是创建了值,那就直接使用
        return instance;
    }
5. 完整代码示例与验证

将以上所有步骤组合起来,就得到了一个基础的单例模式实现。

这种实现方式被称为"懒汉式",因为它是等到第一次需要时才创建实例

  • 完整实现
  • 验证代码
java 复制代码
// 验证逻辑
public static void main(String[] args) {
    Singleton singletonOne = Singleton.getInstance();
    Singleton singletonTwo = Singleton.getInstance();

    if (singletonOne.equals(singletonTwo)) {
        // 在Java中,对于引用类型,== 和 .equals() 在未重写时行为一致,都比较地址
        System.out.println("singletonOne 和 singletonTwo 代表的是同一个实例");
    } else {
        System.out.println("singletonOne 和 singletonTwo 代表的是不同实例");
    }
}
  • 预期结果: 程序会输出"singletonOne 和 singletonTwo 代表的是同一个实例",从而证明了单例模式的正确性。
相关推荐
Lei活在当下3 小时前
【现代 Android APP 架构】09. 聊一聊依赖注入在 Android 开发中的应用
java·架构·android jetpack
不穿格子的程序员3 小时前
从零开始刷算法-栈-括号匹配
java·开发语言·
lkbhua莱克瓦244 小时前
Java练习-正则表达式 1
java·笔记·正则表达式·github
yue0084 小时前
C#类继承
java·开发语言·c#
Larry_Yanan4 小时前
QML学习笔记(五十)QML与C++交互:QML中单例C++对象
开发语言·c++·笔记·qt·学习·ui·交互
im_AMBER4 小时前
算法笔记 09
c语言·数据结构·c++·笔记·学习·算法·排序算法
凯芸呢4 小时前
Java中的数组(续)
java·开发语言·数据结构·算法·青少年编程·排序算法·idea
竹竹零4 小时前
JacksonUtil--序列化与反序列化
java·开发语言·windows
钱多多_qdd4 小时前
基础篇:IoC(三):Bean实例化策略InstantiationStrategy
java·spring