软件设计模式系列之六——单例模式

1 模式的定义

单例模式(Singleton Pattern)是一种常见的创建型设计模式,其主要目的是确保一个类只有一个实例,并提供一个全局访问点来获取该实例。这意味着无论何时何地,只要需要该类的实例,都会返回同一个实例,而不是创建多个相同的实例。单例模式通常用于管理全局状态、资源共享或限制某些资源的访问。

2 举例说明

在日常生活中,随处可见单例模式的例子,比如你家中有一台电视,通常只需要一个遥控器来控制它。无论家里谁想看电视,都会使用同一个遥控器,而且遥控器只能让家里人轮流使用,也就是不能有多个人同时使用遥控器控制电视。这个遥控器就是一个单例,因为它确保只有一个实例存在,并且提供了一个全局的访问点,以便你可以随时使用它。

3 结构

单例模式的结构包括以下要素:
在这里插入图片描述

  • 单例类(Singleton Class):单例模式的核心是单例类,它负责管理唯一的实例。通常,这个类会将其构造函数设为私有,以防止外部直接实例化多个对象。单例类会定义一个静态方法或变量来获取或创建唯一的实例。

  • 私有构造函数(Private Constructor):单例类的构造函数通常会被设置为私有,这样外部无法直接实例化这个类。私有构造函数的目的是确保只有单例类内部可以创建类的实例。

  • 静态成员变量(Static Member Variable):单例类会包含一个私有的静态成员变量,用于保存唯一的实例。这个成员变量通常被命名为 instance 或类似的名称。

  • 静态方法(Static Method):单例类会提供一个公共的静态方法,通常命名为 getInstance() 或类似的名称,用于获取或创建唯一的实例。这个方法会检查是否已经存在实例,如果存在则返回现有实例,否则创建一个新的实例并返回它。

单例模式的关键是将构造函数私有化,以确保只有一个实例,并提供一个全局的方法来获取这个实例,以实现全局唯一性。这种结构确保了在应用程序中只有一个实例存在,无论何时何地都可以访问这个实例,从而实现了单例模式的设计目标。

4 实现步骤

实现单例模式的关键步骤通常包括以下几个:

  1. 将构造函数私有化(Private Constructor):在单例模式中,首先需要将单例类的构造函数设为私有,以防止外部直接实例化多个对象。这是确保只有一个实例的重要步骤。

  2. 创建一个私有的静态成员变量(Private Static Member Variable):单例类内部通常会包含一个私有的静态成员变量,用于保存唯一的实例。这个变量通常被命名为 instance 或类似的名称。

  3. 提供一个公共的静态方法(Public Static Method):单例类会提供一个公共的静态方法,通常命名为 getInstance() 或类似的名称,用于获取或创建唯一的实例。这个方法会检查是否已经存在实例,如果存在则返回现有实例,否则创建一个新的实例并返回它。

  4. 在获取实例时进行实例化(Lazy Initialization):在 getInstance() 方法中,需要检查 instance 是否为 None,如果为 None,则创建一个新的实例并将其赋值给 instance,否则直接返回 instance。这确保了实例在需要时才会被创建,避免了不必要的开销。

  5. 处理多线程环境(Thread Safety):如果应用程序可能在多线程环境下使用单例类,需要考虑线程安全性。可以使用加锁机制来确保在多线程环境下也只有一个实例被创建。

5 代码实现

在Java中,可以使用懒汉式和饿汉式两种方式来实现单例模式。下面分别给出这两种方式的示例代码:
在这里插入图片描述

懒汉式单例模式
在懒汉式中,实例是在首次被请求时才创建。

public class LazySingleton {private static LazySingleton instance;private LazySingleton() {// 私有构造函数,防止外部实例化}public static LazySingleton getInstance() {if (instance == null) {instance = new LazySingleton();}return instance;}
}

在懒汉式中,getInstance 方法首先检查实例是否已经创建。如果没有创建实例,则创建一个新的实例并返回。这种实现延迟了实例的创建,只有在需要时才会创建。

饿汉式单例模式
在饿汉式中,实例在类加载时就被创建,无论是否需要。

public class EagerSingleton {private static final EagerSingleton instance = new EagerSingleton();private EagerSingleton() {// 私有构造函数,防止外部实例化}public static EagerSingleton getInstance() {return instance;}
}

在饿汉式中,实例在类加载时就被创建,因此无论何时需要实例,都可以立即返回。这种实现简单且线程安全,但可能会造成资源浪费,因为实例会在应用程序启动时就被创建。

需要注意的是,懒汉式在多线程环境下需要额外的同步措施来确保线程安全,而饿汉式天生是线程安全的。选择使用哪种方式取决于具体的需求和性能考虑。

6 典型应用场景

单例模式在各种应用场景中都有广泛的应用,主要用于确保一个类只有一个实例,并提供全局访问点。以下是一些常见的单例模式应用场景:
在这里插入图片描述

数据库连接池:在大多数应用程序中,与数据库的交互是常见的操作。为了提高性能和资源利用率,应用程序通常会使用数据库连接池来管理数据库连接。单例模式可以用于确保只有一个数据库连接池的实例存在,以避免多次创建和销毁数据库连接。

线程池:线程池用于管理和控制线程的执行。通过使用单例模式,可以确保只有一个线程池实例,从而更有效地管理并发执行的任务。

配置管理:在应用程序中,通常需要读取和管理配置信息,例如数据库连接参数、应用程序设置等。单例模式可用于存储和管理这些配置数据,以确保在整个应用程序中使用相同的配置。

日志记录器:在应用程序中记录日志是一项重要的任务,通常会使用日志记录器来处理日志信息。通过单例模式,可以确保只有一个日志记录器实例,以避免多次初始化和配置日志记录器。

窗口管理器:在图形用户界面应用程序中,窗口管理器用于管理应用程序窗口的创建、销毁和切换。单例模式可用于确保只有一个窗口管理器实例,以维护窗口状态和顺序。

单例模式在需要确保全局唯一性、资源共享、全局访问和状态管理的各种应用场景中非常有用。它可以帮助简化代码、提高性能,并确保应用程序的一致性。然而,需要谨慎使用,以避免引入全局状态和多线程问题。

7 优缺点

优点:
全局访问点:通过单例模式,可以在应用程序的任何地方轻松访问相同的实例。
资源共享:单例模式可用于管理共享的资源,例如数据库连接、线程池等,以提高性能和资源利用率。
避免重复创建:单例模式确保只有一个实例,避免了重复创建对象的开销。
缺点:
可能引入全局状态:过度使用单例模式可能导致全局状态,使得代码难以维护和测试。
不适用于多线程环境:如果不正确地实现单例模式,可能会导致多线程竞态条件,需要额外的同步机制来解决。

8 类似模式

在软件开发中,单例模式和原型模式通常在创建和管理"bean"(也称为对象或组件)时发挥重要作用,但它们在此上下文中有不同的用途和应用场景。

单例模式在bean的创建中的应用:

Spring框架中的单例bean:在Spring框架中,默认情况下,Spring容器会将Bean配置为单例(Singleton)。这意味着每个bean在应用程序中只有一个实例,并且Spring容器负责管理这些单例bean的生命周期。这种单例模式的应用确保了全局唯一性,并且可以节省资源和提高性能。
原型模式在bean的创建中的应用:

原型范围的Spring bean:在Spring框架中,你可以将bean配置为原型(Prototype)范围,这意味着每次从Spring容器请求该bean时,都会创建一个新的实例。原型模式的应用适用于那些需要频繁创建新实例的场景,例如HTTP请求的处理,每个请求需要一个新的bean实例以避免状态共享。
关系和应用场景:

单例模式通常用于那些需要确保全局唯一性的bean,例如服务层的单例组件、数据库连接池、配置管理器等。它适用于那些需要共享状态或资源的情况。

原型模式通常用于那些需要频繁创建新实例的bean,例如Web应用程序中的请求处理器、线程池中的任务、HTTP会话管理器等。它适用于那些需要隔离状态或资源的情况。

在Spring框架中,你可以根据bean的具体需求将它们配置为单例或原型范围,以满足应用程序的不同要求。这两种模式有各自的优势和适用场景,可以根据业务逻辑和性能要求来选择合适的范围。

9 小结

单例模式是一种有用的设计模式,用于确保一个类只有一个实例,并提供全局访问点。它在多种应用场景中都有用武之地,但需要小心使用,以避免引入全局状态和多线程问题。通过将构造函数私有化、使用静态变量保存实例以及提供一个静态方法来获取实例,可以实现单例模式。在设计应用程序时,要考虑是否需要使用单例模式来满足特定的需求。

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

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

相关文章

vue2以ElementUI为例构建notify便捷精美提示

我们先引入一个 第三方UI库 这里 我们以elementUI为例 先引入依赖 npm install element-ui --save然后 在 main.js 入口文件中 引入一下 import ElementUI from element-ui import element-ui/lib/theme-chalk/index.cssVue.use(ElementUI)然后 在组件中使用 this.$notify({…

如何使用 RunwayML 进行创意 AI 创作

标题:如何使用 RunwayML 进行创意 AI 创作 介绍 RunwayML 是一个基于浏览器的人工智能创作工具,可让用户使用各种 AI 功能来生成图像、视频、音乐、文字和其他创意内容。RunwayML 的功能包括: * 图像生成:使用生成式对抗网络 (…

每日刷题-6

目录 一、选择题 二、算法题 1.Fibonacci数列 2.合法括号序列判断 一、选择题 1、 解析:内联函数是一种可以提高函数执行效率的方法,它的原理是编译时在函数调用点直接展开函数体的代码,从而避免了函数调用的开销。 但是,内联函…

Haproxy负载均衡集群 超详细 (附部署实例)

Haproxy 一、Web集群调度器1.1 常用的Web集群调度器1.2 常用集群调度器的优缺点(LVS ,Nginx,Haproxy)1.2.1 Nginx1.2.2 LVS1.2.3 Haproxy 1.3 LVS、Nginx、Haproxy的区别 二、Haproxy2.1 简介2.2 Haproxy的主要特性2.3 Haproxy应用分析2.4 Haproxy的调度算法(负载均…

牛客: BM5 合并k个已排序的链表

牛客: BM5 合并k个已排序的链表 文章目录 牛客: BM5 合并k个已排序的链表题目描述题解思路题解代码 题目描述 题解思路 合并链表数组中的前两条链表,直到链表数组的长度为一, 返回这个唯一的链表 题解代码 package main/** type ListNode struct{* Val int* Next *ListN…

#循循渐进学51单片机#步进电机与蜂鸣器#not.8

1、能够理解清楚单片机IO口的结构。 2)t1相当于PnP三级管,t2相当于npn三极管 3) 强推挽io具有较强的驱动能力,电流输出能力很强。 2、能够看懂上下拉电阻的电路应用,并且熟练使用上下拉电阻。 3、理解28BYJ-48减速步进电机的工作…

链表oj题 链表与LinkedList 栈的概念 队列的概念 树和二叉树

第 1 题(编程题) 题目名称: 求环的入口点 题目内容: 求环的入口点https://leetcode-cn.com/problems/linked-list-cycle-ii/description/ 第 2 题(编程题) 题目名称: 判断链表带环 题目…

软件设计模式(二):工厂、门面、调停者和装饰器模式

前言 在这篇文章中,荔枝将会梳理软件设计模式中的四种:工厂模式、Facade模式、Mediator模式和装饰器Decorator模式。其中比较重要的就是工厂模式和装饰器模式,工厂模式在开发中使用的频数比较高。希望荔枝的这篇文章能讲清楚哈哈哈哈&#xf…

Ceph入门到精通-ceph对于长文件名如何处理

RADOS object with short name 上一篇博文,我们将介绍了对象相关的数据结构ghobject_t,以及对象在底层文件系统存储的文件名,以及如何从文件名对应到 ghobject_t对象。 映射关系如下图所示: 这里面有一个漏洞,即obje…

【栈与队列面试题】有效的括号(动图演示)

leetcode20.括号匹配问题 前言: 💥🎈个人主页:​​​​​​Dream_Chaser~ 🎈💥 ✨✨刷题专栏:http://t.csdn.cn/UlvTc ⛳⛳本篇内容:力扣上栈与队列的面试OJ题目 目录 leetcode20.括号匹配问题 1.问题描…

华硕电脑怎么录屏?分享实用录制经验!

“华硕电脑怎么录屏呀,刚买的笔记本电脑,是华硕的,自我感觉挺好用的,但是不知道怎么录屏,最近刚好要录一个教程,怎么都找不到在哪里录制,有人能教教我吗?” 随着电脑技术的不断发展…

低代码开源项目整理

低代码是基于可视化和模型驱动理念,结合云原生与多端体验技术,它能够在多数业务场景下实现大幅度的提效降本,为专业开发者提供了一种全新的高生产力开发范式。下面就来分享几个值得学习和使用的前端低代码开源项目,更深入地了解什…

如何实现微服务

一、问题拆解 1.1、客户端如何访问这些服务 原来的Monolithic方式开发,所有的服务都是本地的,UI可以直接调用;现在按功能拆分成独立的服务,跑在独立的虚拟机上的Java进程了。客户端UI如何访问他的? 后台有N个服务&a…

CSS - 鼠标移入整行高亮显示,适用于会员套餐各参数对比页面(display: table,div 转表格形式)

效果图 可根据基础示例和进阶示例&#xff0c;复制进行改造样式。 如下图所示&#xff0c;本文提供 2 个示例。 基础示例 找个 HTML 页面&#xff0c;一键复制运行。 <body><h1 style"text-align: center;">基础示例</h1><section class"…

初试小程序轮播组件

文章目录 一、轮播组件&#xff08;一&#xff09;swiper组件1、功能描述2、属性说明 &#xff08;二&#xff09;swiper-item组件1、功能描述2、属性说明 &#xff08;三&#xff09;轮播组件使用基本语法 二、案例演示&#xff08;一&#xff09;运行效果&#xff08;二&…

JavaEE初阶(5)多线程案例(定时器、标准库中的定时器、实现定时器、线程池、标准库中的线程池、实现线程池)

接上次博客&#xff1a;JavaEE初阶&#xff08;4&#xff09;&#xff08;线程的状态、线程安全、synchronized、volatile、wait 和 notify、多线程的代码案例&#xff1a;单例模式——饿汉懒汉、阻塞队列&#xff09;_di-Dora的博客-CSDN博客 目录 多线程案例 定时器 标准…

9.19-21,openEuler与您相约2023欧洲开源峰会

2023年9月19日-21日&#xff0c;openEuler将参加在西班牙毕尔巴鄂举办的 OSSUMMIT 2023&#xff08;Open Source Summit Europe 2023&#xff09;&#xff0c;这是openEuler继去年正式亮相后的第二次全面参加该峰会。 Open Source Summit Europe是由Linux基金会主办&#xff0…

verdi dump状态机的波形时直接显示状态名

前段时间看到别人用verdi看状态机的波形时&#xff0c;可以显示定义的状态参数&#xff0c;觉得很有意思&#xff0c;特地学习了一下 通常拉出状态机信号的波形是下面这样的 这种信号&#xff0c;我们要想知道每个数值代表的状态&#xff0c;还需要跟定义的parameter比对 像这…

【Flowable】任务监听器(五)

前言 之前有需要使用到Flowable&#xff0c;鉴于网上的资料不是很多也不是很全也是捣鼓了半天&#xff0c;因此争取能在这里简单分享一下经验&#xff0c;帮助有需要的朋友&#xff0c;也非常欢迎大家指出不足的地方。 一、监听器 在Flowable中&#xff0c;我们可以使用监听…

Android 实战项目分享(一)用Android Studio绘制贝塞尔曲线的艺术之旅

一、项目概述 欢迎来到创意之源&#xff01;我们精心打造的绘图应用程序将带你进入一个充满艺术和技术的奇妙世界。通过使用Android Studio&#xff0c;我们实现了绘制贝塞尔曲线的功能&#xff0c;让你能够轻松创作出令人惊叹的艺术作品。不论你是热爱绘画的大学生还是渴望学习…