设计模式学习之——单例模式

文章目录

  • 单例模式
    • 什么叫做单例模式
      • 单例模式的动机
    • 单例模式的引入
      • 思考
    • 饿汉式单例和懒汉式单例
      • 饿汉式单例
      • 懒汉式单例
    • 单例模式总结
      • 1.主要优点
      • 2.主要缺点
      • 3.适用场景

单例模式

什么叫做单例模式

顾名思义,简单来说,单例模式就是只有一个用例的意思
那么官方一点解释是什么:

单例模式是一种设计模式,它确保某个类只有一个实例,并提供一个全局访问点来获取该实例。这种模式在多种编程语言中都有实现,包扩Java和C++。单例模式的实现可以采取饿汉式懒汉式两种方式。**饿汉式是在类加载时就创建了实例,而懒汉式则是在首次使用时才创建实例。**懒汉式在多线程环境下可能会遇到线程安全问题,需要额外的线程安全措施来保证单例对象的唯一性

单例模式的动机

这里有一个问题:Windows的任务管理器无论启动多少次,为什么始终只能弹出一个任务管理器的窗口呢?
也就是说在一个Windows系统中,任务管理器存在唯一性,为什么要这样设计呢?

可以从以下两个方面来分析:

  • 其一,如果能弹出多个窗口,且这些窗口的内容完全一致,全部是重复对象,这势必会浪费系统资源(任务管理器需要获取系统运行时的诸多信息,这些信息的获取需要消耗一定的系统资源,包括CPU资源及内存资源等),而且根本没有必要显示多个内容完全相同的窗口;
  • 其二,如果弹出的多个窗口内容不一致,问题就更加严重了,这意味着在某一瞬间系统资源使用情况和进程、服务等信息存在多个状态,例如任务管理器窗口A显示“CPU使用率”为10%,窗口B显示“CPU使用率”为15%,到底哪个才是真实的呢?这会给用户带来误解,更不可取。

在实际开发中也经常遇到类似的情况,为了节约系统资源,有时需要确保系统中某个类只有唯一一个实例当这个唯一实例创建成功之后,无法再创建一个同类型的其他对象,所有的操作都只能基于这个唯一实例。为了确保对象的唯一性,可以通过单例模式来实现,这就是单例模式的动机所在。

单例模式的引入

模拟实现一下Windows任务管理器
在这里插入图片描述
为了实现WIndows任务管理器的唯一性,通过以下三步对TaskManager类进行重构

  1. 由于每次使用new关键字来实例化TaskManager类时都将产生一个新对象,为了确保TaskManager实例的唯一性,需要禁止类的外部直接使用new来创建对象,因此需要将TaskManager的构造函数的可见性改为private,代码如下:
    在这里插入图片描述

  2. 将构造函数的可见性改为private后,虽然类的外部不能再使用new来创建对象,但是在TaskManager的内部还是可以创建对象的,可见性只对类外有效。因此,可以在TaskManager中创建并保存这个唯一实例。为了让外界可以访问这个唯一实例,需要在TaskManager中定义一个静态的TaskManager类型的私有成员变量,代码如下:
    在这里插入图片描述

  3. 为了保证成员变量的封装性,将TaskManager类型的tm对象的可见性设置为private,但外界该如何使用该成员变量并何时实例化该成员变量呢?答案是增加一个公有的静态方法,代码如下:
    在这里插入图片描述

在getInstance()方法中首先判断tm对象是否存在,如果不存在(即tm==null为true),则使用new关键字创建一个新的TaskManager类型的tm对象,再返回新创建的tm对象;否则直接返回已有的tm对象。
需要注意的是getInstance()方法的修饰符,首先它应该是一个public方法,以便外界其他对象使用;其次它使用了static关键字,即它是一个静态方法,在类外可以直接通过类名来访问,而无须创建TaskManager对象。事实上,在类外也无法创建TaskManager对象,因为构造函数是私有的。

思考

为什么要将成员变量tm定义为静态变量?

通过以上三个步骤,完成了一个个最简单的单例类的设计,其完整代码如下:

class TaskManager{
    private:
    TaskManager(){}//初始化窗口
    public:
    void displayProcess(){}//显示进程
    void displayServices(){}//显示服务
    public:
    static TaskManager* getInstance(){
        if(tm == nullptr)
        {
            tm = new TaskManager();
        }
        return tm;
    }
    private:
    static TaskManager *tm;
};
TaskManager * TaskManager::tm = nullptr;

在类外无法直接创建新的TaskManager对象,但可以通过代码TaskManager::getInstance()访问实例对象。第一次调用getInstance()方法时将创建唯一实例,再次调用时将返回第一次创建的实例。
在这里插入图片描述

在这里插入图片描述

饿汉式单例和懒汉式单例

上面的简单的单例模式存在一个问题,在多线程的场景下,当第一次调用getInstanc()时创建并对象时,tm为nullptr值,因此系统将执行代码 tm = new Taskanager();在这个过程中,如果初始化工作要进行大量工作,则需要一段时间来创建TaskManager对象。而在此时,如果再一次调用getInstance()方法(通常发生在多线程环境中),由于TaskManager对象尚未创建成功,仍为nullptr值,判断条件"tm == nullptr"为真值,因此代码 tm = new TaskManager();会再次执行,导致最终创建了多个tm对象,这违背了单例模式的初衷,也可能会导致发生运行错误。

那么如何解决这个问题呢?

饿汉式单例

在这里插入图片描述

从图中可以看到当类被加载时,静态变量instance会被初始化,此时类的私有构造函数会被调用,单例类的唯一实例将被创建,可确保单例对象的唯一性。

懒汉式单例

在前面的单例模式的引入介绍中,我们用的就是懒汉式的单例模式,但是在多线程的场景中会出现问题,在 C++11 中,静态局部变量这种方式天然是线程安全的,不存在线程不安全的问题。原因是C++ 11标准中新增了一个特性叫Magic Static:如果变量在初始化时,并发线程同时进入到static声明语句,并发线程会阻塞等待初始化结束。
但是为了方便学习我们还是需要在这里引入互斥锁

class LazySingleton
{
private:
    LazySingleton() {}
    ~LazySingleton() {}

private:
    static LazySingleton *instance;
    static std::mutex mutex; // 互斥锁
public:
    static LazySingleton *getInstance()
    {
        if (instance == nullptr)
        {
            std::lock_guard<std::mutex> lock(mutex); // 加锁

            if (instance == nullptr)
            {
                instance = new LazySingleton();
            }
        }
        return instance;
    }
};
LazySingleton* LazySingleton::instance = nullptr;

单例模式总结

单例模式作为一种目标明确、结构简单、理解容易的设计模式,在软件开发中使用频率相当高,在很多应用软件和框架中都得以广泛应用。

1.主要优点

单例模式的主要优点如下:
(1)单例模式提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它。
(2)由于在系统内存中只存在一个对象,因此可以节约系统资源。对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能。
(3)允许可变数目的实例。基于单例模式,开发人员可以进行扩展,使用与控制单例对象相似的方法来获得指定个数的实例对象,既节省系统资源,又解决了由于单例对象共享过多有损性能的问题。(注:自行提供指定数目实例对象的类可称之为多例类。)

2.主要缺点

单例模式的主要缺点如下:
(1)由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
(2)单例类的职责过重,在一定程度上违背了单一职责原则。因为单例类既提供了业务方法,又提供了创建对象的方法(工厂方法),将对象的创建和对象本身的功能耦合在一起。
(3)现在很多面向对象语言(如Java、C#)的运行环境都提供了自动垃圾回收技术,因此,如果实例化的共享对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致共享的单例对象状态的丢失。

3.适用场景

在以下情况下可以考虑使用单例模式:
(1)系统只需要一个实例对象。例如,系统要求提供一个唯一的序列号生成器或资源管理器,或者需要考虑资源消耗太大而只允许创建一个对象。
(2)客户调用类的单个实例只允许使用一个公共访问点。除了该公共访问点,不能通过其他途径访问该实例。

——————————————————————————————————————————————————————————————————————————————————————————————————————————————

📜 参考资料
设计模式的艺术—— 刘伟—— 清华大学出版社 ->链接

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

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

相关文章

VS Code安装及环境配置(超详细)

VS Code简介 Visual Studio Code&#xff08;简称 VS Code &#xff09;是 Microsoft 于2015年4月发布的一款代码编辑器&#xff0c;以界面简洁、轻量著称。 它是一款免费开源的现代化轻量级代码编辑器&#xff0c;支持几乎所有主流开发语言的语法高亮、智能代码补全、自定义…

LLaMA:挑战大模型Scaling Law的性能突破

实际问题 在大模型的研发中,通常会有下面一些需求: 计划训练一个10B的模型,想知道至少需要多大的数据?收集到了1T的数据,想知道能训练一个多大的模型?老板准备1个月后开发布会,给的资源是100张A100,应该用多少数据训多大的模型效果最好?老板对现在10B的模型不满意,想…

游戏遇到攻击有什么办法能解决?

随着网络技术的飞速发展&#xff0c;游戏行业在迎来繁荣的同时&#xff0c;也面临着日益严峻的网络威胁。黑客攻击、数据泄露、DDoS攻击等安全事件频发&#xff0c;给游戏服务器带来了极大的挑战。面对愈演愈烈的网络威胁&#xff0c;寻找一个能解决游戏行业攻击问题的安全解决…

C++STL 初阶(5)vector的简易实现(上)

不同于string只实现一个最简单的版本&#xff0c;vector在此处我们要实现的是模版类&#xff0c;类模版的声明和定义分离非常不方便&#xff08;会在链接时报错&#xff09;&#xff0c;所以我们都只在一个vector.h下去实现声明和定义。后续我们提及到的库里面实现的vector也是…

Go 与 Java 字符编码选择:UTF-8 与 UTF-16 的较量

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

Chrome插件: Octotree让你GitHub代码浏览速度飙升

在GitHub上浏览和管理项目代码时&#xff0c;您是否曾为复杂的目录结构感到困惑&#xff1f;如果有一种工具能够让您轻松浏览项目的文件和目录&#xff0c;会不会大大提升您的工作效率&#xff1f;这正是Octotree浏览器插件所能做到的。 不过说实话&#xff0c;GitHub自带的代码…

stm32学习笔记---GPIO输入(理论部分)

目录 GPIO输入模式下的硬件和电路 按键原理 传感器原理 什么是上下拉电阻&#xff1f; 运算放大器当做比较器 按键的硬件电路 传感器的硬件电路 STM32用到的C语言知识 STM32中的C语言数据类型 C语言中的宏定义 typedef和define的区别是什么&#xff1f; C语言的枚举…

云计算期末综合测试题

云计算综合测试题 单选题填空题判断题简答题 单选题 这里选择题&#xff0c;直接以填空题展示&#xff0c;并给出解析 Bigtable是&#xff08;Google&#xff09;开发的分布式存储系统 解析&#xff1a;分布式结构化数据表Bigtable是Google基于GFS和Chubby开发的分布式存储系统…

FlowUs2024重磅革新预告:RAG技术赋能『问问AI』,笔记变现新纪元等你开启!

&#x1f389; 在FlowUs的广阔天地间&#xff0c;知识的边界被无限拓展&#xff0c;引领着一场场创新与收获的庆典&#xff01;&#x1f680; 随着一年间不断的精进与革新&#xff0c;FlowUs与众多用户并肩前行&#xff0c;在追求极致体验的道路上迈出坚实步伐。步入2024年&am…

STM32CubeIDE提示找不到头文件(No such file or directory)的解决办法

0 前言 最近在使用STM32CubeIDE时&#xff0c;发现为工程添加了头文件路径&#xff0c;但编译的时候还是报错&#xff0c;提示找不到头文件&#xff1a; 1 解决办法 1.1 为工程添加头文件路径 右键我们的工程&#xff0c;然后添加头文件路径&#xff08;最好是相对路径&am…

【开发】内网穿透ztncui搭建私有节点

文章目录 写在前面一键部署ztnuci记录后续 写在前面 前面搭建moon节点转发的确会降低延迟&#xff0c;但是总有出现moon节点解析不成功的例子&#xff0c;于是疯狂寻找答案是为什么&#xff1f;终于在知乎上找到这样一个答案。 一键部署ztnuci 参考这篇很完善的教程和贴心的…

Temu(拼多多跨境电商) API接口:获取商品详情

核心功能介绍——获取商品详情 在竞争激烈的电商市场中&#xff0c;快速、准确地获取商品数据详情对于电商业务的成功至关重要。此Temu接口的核心功能在于其能够实时、全面地获取平台上的商品数据详情。商家通过接入Temu接口&#xff0c;可以轻松获取商品的标题、价格、库存、…

【已解决】在IDEA中使用Git拉取代码时提示:Can‘t update / master has no tracked branch

文章目录 问题描述原因分析解决方案 问题描述 在IDEA中使用Git拉取代码&#xff0c;尝试更新本地项目代码&#xff0c;提示 " Cant update / master has no tracked branch "&#xff0c;如下图所示&#xff1a; 原因分析 出现上述问题意味着本地名为master的分支&…

如何降低MCU系统功耗?

大家在做MCU系统开发的时候&#xff0c;是否也碰到过降低MCU系统功耗的需求&#xff1f; MCU系统整板功耗是个综合的数据&#xff0c;包括MCU功耗以及外部器件功耗&#xff0c;在此我们主要介绍如何降低MCU的功耗&#xff1a; 可以在满足应用的前提下&#xff0c;降低MCU的运…

仿饿了么加入购物车旋转控件 - 自带闪转腾挪动画 的按钮

, mWidth - mCircleWidth, mHeight - mCircleWidth); canvas.drawRoundRect(rectF, mHintBgRoundValue, mHintBgRoundValue, mHintPaint); //前景文字 mHintPaint.setColor(mHintFgColor); // 计算Baseline绘制的起点X轴坐标 int baseX (int) (mWidth / 2 - mHintPaint.m…

数据结构与算法引入(Python)

华子目录 引入第一次尝试第二次尝试 算法的概念算法的五大特性 算法效率衡量执行时间单靠时间值绝对可信吗&#xff1f; 时间复杂度与 "大O记法"如何理解 “大O记法” 最坏时间复杂度时间复杂度的几条基本计算规则 算法分析常见的时间复杂度常见时间复杂度之间的关系…

【进阶篇-Day5:JAVA常用API的使用(Math、BigDecimal、Object、包装类等)】

目录 1、API的概念2、Object类2.1 Object类的介绍2.2 Object的toString()方法2.3 Object的equals()方法2.4 Objects概述 3、Math类4、System类5、BigDecimal类6、包装类6.1 包装类的概念6.2 几种包装类&#xff08;1&#xff09;手动转换包装类&#xff1a;&#xff08;2&#…

前端Web开发HTML5+CSS3+移动web视频教程 Day1

链接 HTML 介绍 写代码的位置&#xff1a;VSCode 看效果的位置&#xff1a;谷歌浏览器 安装插件 open in browser&#xff1a; 接下来要保证每次用 open in browser 打开的是谷歌浏览器。只需要将谷歌浏览器变为默认的浏览器就可以了。 首先进入控制面板&#xff0c;找到默…

实时语音翻译软件哪个好?多语言实时沟通无负担

众所周知&#xff0c;英语是国际交流的共同语言。无论是跨国商务洽谈还是学术研讨&#xff0c;英语的流畅使用至关重要。 然而&#xff0c;语言的障碍依然存在&#xff0c;这时一款高效的英语语音翻译软件就显得尤为关键。它们能够即时转换语言&#xff0c;让我们的对话无国界…

Java模拟马尔可夫链类问题的验证

马尔可夫链&#xff08;Markov Chain, MC&#xff09;是概率论和数理统计中具有马尔可夫性质&#xff08;Markov property&#xff09;且存在于离散的指数集&#xff08;index set&#xff09;和状态空间&#xff08;state space&#xff09;内的随机过程&#xff08;stochasti…