【再谈设计模式】单例模式~唯一性的守护者

一、引言

        在软件工程中,软件开发,设计模式是提高代码复用性和可维护性的有效工具。单例模式(Singleton Pattern)作为一种创建型设计模式,旨在确保一个类只有一个实例,并提供对该实例的全局访问。这一模式在许多场景中都显得尤为重要,尤其是在需要共享资源或协调系统状态时。

二、定义与描述

        单例模式的核心思想是限制一个类仅能实例化一次,并提供一个全局访问点。这样可以避免重复实例化导致的资源浪费和状态不一致的问题。

结构

单例模式通常包含以下几个组成部分:

  • 私有构造函数:防止外部类直接实例化。
  • 一个静态变量:持有唯一的实例。
  • 一个静态方法:用于获取该实例。

三、抽象背景

        单例模式的背景可以追溯到多个设计需求的实际应用。例如,当一个应用程序需要使用一个共享的配置文件、数据库连接或共享资源时,单例模式便成为一个很好的选择。在这种情况下,程序不能允许多个实例同时存在,以免造成数据污染和不一致。

四、适用场景与现实问题解决

单例模式常用于以下场景:

  • 配置管理:在整个应用中共享配置文件或参数。
  • 日志管理:全局共享的日志记录器。
  • 数据库连接池:管理数据库连接的共享实例。

  • 线程池管理:确保线程池在整个应用中只有一个实例。

       通过应用单例模式,可以有效避免各类资源的误用和浪费,从而提升程序的性能和稳定性。旨在确保一个类只有一个实例,并提供一个全局访问点。为了让这个概念更易于理解,我们可以使用一个生活中的实际问题来说明。

生活例子:办公室的打印机

        在一个办公室工作,办公室里有一台打印机。现在我们来看看这台打印机在使用中的几种情况:

        唯一性:这个办公室只能有一台打印机。想象一下,如果有多台打印机,可能会导致很多问题,比如文件打印到错误的打印机、打印任务混乱等。因此,我们需要确保只有一台打印机。

        共享访问:办公室里的每个人都需要使用这台打印机,无论是打印文件还是复印资料。因此,我们需要一个简单的方法让每个人都能访问这台打印机,而不需要每次都去找。

        节省资源:维护多台打印机的成本和管理会耗费大量时间和精力。只有一台打印机可以集中管理,更加高效。

单例模式的类比

        在编程中,单例模式就像这台办公室的打印机一样。它确保一个类(例如打印机类)只有一个实例,并提供一个全局的访问点。在代码中实现单例模式通常涉及以下几个步骤:

  • 私有构造函数:防止其他地方直接创建新实例。
  • 静态变量:用于保存唯一的实例。
  • 公共静态方法:提供访问该实例的方式。

        通过这种方式,代码中需要使用打印机的地方,都会获取到同一个实例,避免了实例重复,确保了全局统一性。

        单例模式就像是确保办公室只有一台打印机,避免了资源的浪费和管理的复杂性,让每个人都可以方便地访问这台打印机。而在实际编程中,单例模式则确保了一个类的唯一性和资源的高效利用。

五、初衷与问题解决

        单例模式的初衷是为了控制对象的数量,使其不被无意创建。许多开发者面临对象管理不当导致的资源浪费和状态不一致的问题,而这一模式恰好能够有效解决这些问题。单例模式实现了对全局资源的集中管理,提高了系统的可维护性和安全性。

六、各语言实现与编码示例

Java 实现

public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

Go 实现

package main

import "sync"

var instance *Singleton
var once sync.Once

type Singleton struct{}

func GetInstance() *Singleton {
    once.Do(func() {
        instance = &Singleton{}
    })
    return instance
}

C++ 实现

class Singleton {
private:
    static Singleton* instance;
    Singleton() {}
public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }
};

Singleton* Singleton::instance = nullptr;

Python 实现

class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
        return cls._instance
    
singleton1 = Singleton()
singleton2 = Singleton()

assert singleton1 is singleton2  # True

七、单例模式的优缺点

优点

  • 节省内存:同一时刻只存在一个实例。
  • 全局访问:提供全局访问点,方便在程序的不同部分共享资源。
  • 控制资源使用:通过限制实例化次数,可以避免资源浪费。

缺点

  • 可扩展性差:在某些场景下可能会导致系统中的“单点故障”。
  • 更难测试:单例模式可能使得测试变得复杂,因为它引入了全局状态。
  • 潜在的线程安全问题:在多线程环境中需谨慎处理,可能需要额外的同步机制。

八、单例模式的升级版

        在实际应用中,单例模式可能会演变成更复杂的形式,包括饿汉单例和懒汉单例。

饿汉单例

        饿汉单例在类被加载时就创建实例,因此实例的创建是提前的。通常使用一个静态常量来存储单例的实例。

特点

  • 线程安全:实例在类加载时就创建,因此不需要额外的同步机制。
  • 容易实现:比较简单,没有复杂的逻辑。
  • 资源浪费:即使没有使用,也会在加载时就创建实例,这可能导致资源浪费。

示例代码

public class HungrySingleton {
    // 在静态变量中创建唯一的实例
    private static final HungrySingleton INSTANCE = new HungrySingleton();

    // 私有构造函数,防止外部创建对象
    private HungrySingleton() {}

    // 提供一个全局访问点
    public static HungrySingleton getInstance() {
        return INSTANCE;
    }
}

懒汉单例

        懒汉单例在第一次被调用时才会创建实例,因此实例的创建是延迟的。通常会使用一个私有静态变量来存储实例,并在访问时检查是否已经被创建。

特点

  • 延迟加载:只有在需要的时候才创建实例,如果不需要,则不会创建。
  • 线程不安全:如果多个线程同时访问 getInstance() 方法,可能会导致创建多个实例。为了解决这个问题,常常在方法上添加同步机制。
  • 实现复杂:需要考虑多线程访问的安全性和性能。

示例代码(线程不安全):

public class LazySingleton {
    private static LazySingleton instance;

    private LazySingleton() {}

    public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

示例代码(线程安全,使用同步):

public class ThreadSafeLazySingleton {
    private static ThreadSafeLazySingleton instance;

    private ThreadSafeLazySingleton() {}

    public static synchronized ThreadSafeLazySingleton getInstance() {
        if (instance == null) {
            instance = new ThreadSafeLazySingleton();
        }
        return instance;
    }
}
  • 饿汉单例适合于实例创建比较简单,并且应用程序总是需要这个实例的情况。
  • 懒汉单例适合于实例创建开销比较大,或者在特定条件下才需要创建的情况,但需要注意线程安全的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/903223.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何在 Elasticsearch Ruby 客户端中使用 ES|QL Helper

作者:来自 Elastic Fernando Briano 了解如何使用 Elasticsearch Ruby 客户端编写 ES|QL 查询并处理其结果。 简介 Elasticsearch Ruby 客户端可用于编写 EQ|QL 查询,使处理从 esql.query 返回的数据更加容易。ES|QL 允许开发人员通过查询过滤、转换和分…

redis详细教程(3.ZSet,Bitmap,HyperLogLog)

ZSet Redis 的 ZSet(有序集合)是一种特殊的数据类型,它允许存储一系列不重复的字符串元素,并为每个元素关联一个分数(score)。这个分数用于对集合中的元素进行排序。ZSet 的特点是: 唯一性&am…

MYSQL-SQL-03-DQL(Data Query Language,数据查询语言)(单表查询)

DQL(数据查询语言) DQL英文全称是Data Query Language(数据查询语言),数据查询语言,用来查询数据库中表的记录。 查询关键字: SELECT 在一个正常的业务系统中,查询操作的频次是要远高于增删改的,当我们去访…

Cisco Packet Tracer 8.0 路由器的基本配置和Telnet设置

文章目录 构建拓扑图配置IP地址配置路由器命令说明测试效果 构建拓扑图 1,添加2811路由器。 2,添加pc0。 3,使用交叉线连接路由器和pc(注意线路端口)。 4,使用配置线连接路由器和pc(注意线路…

优化网站结构提升用户体验的关键要素

内容概要 在数字时代,网站的架构和用户体验密切相关。一个合理的网站结构不仅能帮助用户快速找到所需信息,还能提升整体的访问满意度。为了达到这一目的,网站需要强调几个关键要素。 首先,清晰的导航设计至关重要。导航应当直观…

Android Gradle

#1024程序员节|征文# Gradle 是一款强大的自动化构建工具,广泛应用于 Android 应用开发。它通过灵活的配置和丰富的插件系统,为项目构建提供了极大的便利。本文只是简单的介绍 Gradle 在 Android 开发中的使用,包括其核心概念、构…

微积分复习笔记 Calculus Volume 1 - 3.8 Implicit Differentiation

3.8 Implicit Differentiation - Calculus Volume 1 | OpenStax

Java——lambda表达式和StreamAPI

一、lambda 1. lambda表达式 1.1 Lambda表达式的使用举例: (o1,02)->Integer.compare(o1,o2); 1.2 Lambda表达式的格式举例: Lambda形参列表->lambda 1.3 Lambda表达式的格式 lambda操作符或箭头操作符 的左边:lambda形参列表,对应着要重写的接口中的…

django游戏门户系统

想做毕业设计但还没有头绪?🙋‍♂️django游戏门户系统了解一下!这个系统不仅功能全面,还能轻松解决你的项目选题难题! 我们这个基于Django开发的游戏门户系统提供了用户注册、登录、内容发布以及管理功能&#xff0c…

大数据日志处理框架ELK方案

介绍应用场景大数据ELK日志框架安装部署 一,介绍 大数据日志处理框架ELK(Elasticsearch、Logstash、Kibana)是一套完整的日志集中处理方案,以下是对其的详细介绍: 一、Elasticsearch(ES) 基本…

【SQL实验】表的更新和简单查询

完整代码在文章末尾 在上次实验创建的educ数据库基础上,用SQL语句为student表、course表和sc表中添加以下记录 【SQL实验】数据库、表、模式的SQL语句操作_创建一个名为educ数据库,要求如下: (下面三个表中属性的数据类型需要自己设计合适-CSDN博客在这篇博文中已经…

LeetCode反转链表

题目描述 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 示例 1: 输入:head [1,2,3,4,5] 输出:[5,4,3,2,1] 示例 2: 输入:head [1,2] 输出:[2,1] 示例 3&#…

011:软件卸载工具TotalUninstall安装教程

摘要:本文详细介绍软件卸载工具TotalUninstall安装流程。 一、软件介绍 TotalUninstall是一款功能强大的卸载与清理工具,它能够彻底卸载不需要的应用程序,并清除相关的注册表项、文件残留和临时文件,确保系统干净无残留&#xff…

美畅物联丨视频上云网关如何配置上级联网云平台

在当今的智慧交通与安防监控体系中,视频上云网关发挥着至关重要的作用。以美畅视频上云网关为例,具备强大的兼容性,能够对接来自不同厂家、不同型号的视频设备,将这些设备输出的各异视频流进行汇聚整合。在获取摄像机视频流后&…

深入理解JavaScript:两大编程思想和ES6类以及对象概念解析

文章目录 两大编程思想ES6中的类和对象 两大编程思想 面向过程 (Procedural-Oriented Programming,POP) 定义:面向过程的编程是一种基于过程调用的编程范式,它将程序看作是一系列函数或过程的集合。每个函数负责完成…

推荐一个好用的VSCode插件

还在花馒头使用 Copilot?别再做大冤种啦! 现在有个更好用的AI编程助手--豆包 MarsCode!它不仅完全免费,而且功能强大,让你在编程时得心应手!再也不用担心高昂的订阅费用,省下来的馒头&#xff…

衡石分析平台系统分析人员手册-图表查询应用

查询应用​ 在业务分析过程中,查询明细数据有时需要满足如下场景: 在自助化的操作界面中用户可以自主选择查询字段及相应的筛选条件进行查询。用户通过简单的鼠标点击能够快速获得所需数据,并提供聚合计算等高级功能。 上述场景可以通过查…

数据结构与算法-21算法专项(中文分词)(END)

中文分词 搜索引擎是如何理解我们的搜索语句的? mysql中使用 【like “%中国%”】,这样的使用方案 缺点1:mysql索引会失效缺点2:不能模糊,比如我搜湖南省 就搜不到湖南相关的 1 trie树 Trie树,又称前缀树…

C++ 中的可调用对象

目录 一.可调用对象简介 1.什么是可调用对象? 2.可调用对象有什么用? 二.函数指针和仿函数 1.函数指针 a.函数指针的使用语法 b.函数指针的应用场景 2.仿函数 a.仿函数的基本概念 b.仿函数的优点 三.lambda表达式和function 1.lambda表达式 …

完全了解一个asp.net core MVC项目模板

当我们使用Visual Studio 2022去新建一个基于asp.net core Web项目的时候,一般有三种选择,一种是空项目,一种是基于MVC的项目、再有一种就是基于包含Razor Pages实例的web应用。如下图: 今天,我们打算选择基于MVC模…