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

文章目录

  • 单例模式
    • 什么叫做单例模式
      • 单例模式的动机
    • 简单单例模式
      • 思考
    • 饿汉式单例和懒汉式单例
      • 饿汉式单例
      • 懒汉式单例
    • 单例模式总结
      • 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)客户调用类的单个实例只允许使用一个公共访问点。除了该公共访问点,不能通过其他途径访问该实例。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python语言修改控制台输出文字的颜色和背景颜色

Python语言修改控制台输出文字的颜色和背景颜色 格式显示模式字体颜色背景颜色文字加效果显示类 格式 \033[显示模式;字体颜色;背景颜色m 显示模式 显示模式格式将文本颜色和背景颜色重置为默认值&#xff0c;取消所有其他文本属性\033[0m高亮&#xff08;加粗&#xff09;\03…

揭秘银行小企业贷款业务:功能测试的全面指南

银行小企业贷款业务是指银行为支持小型企业的发展&#xff0c;向其提供的按照规定利率和期限的一种借款方式。这种贷款主要用于满足小企业进行固定资产购建、技术改造等大额长期投资的需求&#xff0c;同时也帮助解决小企业在发展过程中遇到的“融资难”、“融资贵”等问题。 …

速盾:什么是DDoS攻击,并如何应对?

在当今的网络世界中&#xff0c;DDoS&#xff08;Distributed Denial of Service&#xff0c;分布式拒绝服务&#xff09;攻击是一种极具威胁性的网络安全问题。作为一名专业程序员&#xff0c;深入了解 DDoS 攻击的本质和应对方法至关重要。 DDoS 攻击是指攻击者利用大量傀儡计…

SparkOnHive_数据多列转行操作应用

前言 行专列&#xff0c;列转行是数开不可避免的一步&#xff0c;尤其是在最初接触Hive的时候&#xff0c;看到什么炸裂函数&#xff0c;各种udf&#xff0c;有点发憷&#xff0c;无从下手&#xff0c;时常产生这t怎么搞&#xff0c;我不会啊&#xff1f; 好吧&#xff…

Study--Oracle-04-SQL练习

一、SQL语句思维导图 二、SQL练习 -- 以employee_id 为排序&#xff0c;列出前5个人 -- FETCH select employee_id,first_name from employees order by employee_id FETCH FIRST 5 rows only; -- 以employee_id 为排序&#xff0c;从第6个人开始 到第10个人 -- offset …

【Java】已解决java.sql.SQLException异常

文章目录 一、分析问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决java.sql.SQLException异常 在Java中&#xff0c;java.sql.SQLException是一个通用的异常类&#xff0c;用于表示在数据库操作中发生的错误。无论是类型错误、数据类型不匹配…

ArcGIS图斑分区(组)排序—从上到下从左到右

​​ 点击下方全系列课程学习 点击学习—>ArcGIS全系列实战视频教程——9个单一课程组合系列直播回放 ArcGIS图斑分区&#xff08;组&#xff09;从上到下从左到右排序 是之前的内容的升级 GIS技巧100例——12ArcGIS图斑空间排序 关于今天的内容 我们在19年已经和大家分…

Iptables(1)基本概念

简介 iptables 是一个用于 Linux 操作系统的包过滤防火墙工具,可帮助管理网络流量和实施安全策略。它允许用户配置规则集以控制数据包如何在计算机上移动和处理。通过定义规则来允许或拒绝特定类型的流量,iptables 可以提供有效的网络安全保护。 主要功能包括: 数据包过滤…

mkfs.ext4 -- 生成ext4文件系统

mkfs.ext4 介绍 mkfs.ext4 是一个在 Linux 系统上用于创建 ext4 文件系统的工具。ext4 文件系统是扩展的第三代文件系统 (Fourth Extended Filesystem)&#xff0c;是 ext3 的改进版&#xff0c;提供了更好的性能和功能。下面是对 mkfs.ext4 工具的详细讲解&#xff0c;包括其…

计算机视觉实验二:基于支持向量机和随机森林的分类(Part one: 编程实现基于支持向量机的人脸识别分类 )

目录 一、实验内容 二、实验目的 三、实验步骤 四、实验结果截图 五、实验完整代码 六、报错及解决方案 PS:实验的运行速度受电脑性能影响,如遇运行卡顿请耐心等待。 一、实验内容 编程实现基于支持向量机的人脸识别分类,基本功能包括:Labeled Faces in th…

【Kubernetes项目部署】k8s集群+高可用、负载均衡+防火墙

项目架构图 &#xff08;1&#xff09;部署 kubernetes 集群 详见&#xff1a;http://t.csdnimg.cn/RLveS &#xff08;2&#xff09; 在 Kubernetes 环境中&#xff0c;通过yaml文件的方式&#xff0c;创建2个Nginx Pod分别放置在两个不同的节点上&#xff1b; Pod使用hostP…

Docker环境离线安装

Docker环境离线安装 下载下列.deb包 sudo *.deb

【前端面经】滴滴一面

1.闭包是什么? 闭包的用途? JS 闭包 2024-3-12 闭包是指在一个函数内部定义的函数&#xff0c;该内部函数可以访问其外部函数的变量和参数。即使外部函数已经执行完毕并返回了&#xff0c;内部函数依然能够访问这些变量和参数。 //举例 function outerFunction(outerVariab…

windows系统中开发的GO程序生成docker镜像并部署到阿里云服务(linux系统)的操作说明

本文简述将go程序生成docker镜像的操作方法&#xff0c;以及如何部署到阿里云服务。其中go程序在windows系统中开发&#xff0c;阿里云服务的操作系统为linux&#xff08;centos7.9&#xff09;&#xff0c;以下为流程示意图&#xff1a; 一、window系统中开发go程序 程序实现…

前端技术栈三(vue+Axios)

一、Vue 1 基本介绍 1.1 Vue 是什么? Vue (读音 /vjuː/&#xff0c;类似于 view) 是一个前端框架, 易于构建用户界面 Vue 的核心库只关注视图层&#xff0c;不仅易于上手&#xff0c;还便于与第三方库或项目整合 支持和其它类库结合使用 开发复杂的单页应用非常方便 Vue 是…

RAM + 串口的简单应用

REVIEW 之前已经学习过&#xff1a; RAM&#xff1a; RAM IP核配置_ip核 ram配置-CSDN博客 串口接收&#xff1a;Vivado 串口接收优化-CSDN博客 串口发送&#xff1a;Vivado 串口通信(UART)------串口发送_vivado串口收发实验-CSDN博客 按键&#xff1a;基于状态机的按键消抖实…

ADOP带你了解:数据中心的高速互联解决方案

随着大语言模型和AIGC的飞速发展&#xff0c;数据中心对于高速、高可靠性的网络连接需求日益增长。ADOP系列产品正是在这样的背景下应运而生&#xff0c;为现代数据中心提供了全面的连接解决方案。 ADOP系列产品概览 ADOP系列产品旨在为云、高性能计算、Web 2.0、企业、电信、…

SparkSQL的分布式执行引擎-Thrift服务:学习总结(第七天)

系列文章目录 SparkSQL的分布式执行引擎 1、启动Thrift服务 2、beeline连接Thrift服务 3、开发工具连接Thrift服务 4、控制台编写SQL代码 文章目录 系列文章目录前言一、SparkSQL的分布式执行引擎(了解)1、启动Thrift服务2、beeline连接Thrift服务3、开发工具连接Thrift服务4、…

深入理解 PHP 魔术常量

PHP 魔术常量是 PHP 语言中预先定义的常量&#xff0c;它们具有特殊的含义和用途。这些常量的值在程序运行时会根据上下文自动确定&#xff0c;为开发人员提供了方便和灵活性。本文将从多个方面深入探讨 PHP 魔术常量&#xff0c;包括它们的定义、特点、用途以及使用注意事项。…

(7)摄像机和云台

文章目录 前言 1 云台 2 带有MAVLink接口的摄像机 3 相机控制和地理标签 4 视频质量差的常见修复方法 5 详细主题 前言 Copter、Plane 和 Rover 最多支持 3 轴云台&#xff0c;包括自动瞄准感兴趣区域&#xff08;ROI&#xff09;的相机和自动触发相机快门等先进功能。按…