系统架构技能之设计模式-单件模式

news2024/12/22 2:01:56

一、开篇

其实我本来不是打算把系统架构中的一些设计模式单独抽出来讲解的,因为很多的好朋友也比较关注这方面的内容,所以我想通过我理解及平时项目中应用到的一

些常见的设计模式,拿出来给大家做个简单讲解,我这里只是抛砖引玉,如果某个地方讲解的不正确或者不详细,请大家批评指出。园子里面的很多的大牛写的设计模式

都非常的经典,我这里写可能有点班门弄斧的感觉,不过我还是决定把它写出来,希望能对初学者有一定的帮助和指导的作用。当然我这里如果说某个地方解释的有问

题或者说是某个地方写的不符合逻辑之处,还请大家多多指出,提出宝贵意见。

软件工程中其实有很多总结性的话语,比如说软件=算法+数据结构等等这样的描述,当然我们这里可能算法就是泛指一些软件中的编程方法了,设计模式怎么去

理解呢?为什么要有设计模式?它能带来什么?等等这些都是我们需要讨论的问题。首先我们需要知道设计模式能带来什么。可能这才是我们学习它的主要原因,如果

说不能为我们在书写软件的过程中带来更方面的好处,那我们也不会使用和学习它。

设计模式是什么?

设计模式可以简单的理解为解决某一系列问题的完美的解决方案。我们在软件开发的过程中经常遇到设计功能实现的问题,而设计模式正是为了解决软件设计功能

实现时遇到的某一类问题的解决方案。因为一般情况下来说,我们在某个软件功能的开发过程中遇到的功能设计问题,可能是前人很早就遇到过的问题,所以通过这种

设计模式的方式来解决,能让我们在软件实现的过程中少走弯路,或者说是给我们的软件设计带来很好的灵活性和适应性。

设计模式带来了什么?

设计模式是源于实践,并且每种设计模式都包含了一个问题描述,问题涉及到的参与者并且提供了一个实际的解决方案。设计模式的好处我们可以通过下图来简单

说明:

image 当然我这里可能总结还不完全,还请大家补充,我会更新这里面的内容。当然设

计模式带来了这么多的好处,所以我们学习设计模式就显得比较必要了,也是从事软件开发及设计必须掌握的基本技能之一。

设计模式的简单分类:

image 当然这里可以简单的分为这3大类,下面我们在讲述的过程中将会分别讲解,当然我这里是以创建型模

式开始讲解,我想创建型模式也是大家项目中必备的吧?下面我就从创建型模式先来讲解。

二、摘要

本文将主要讲解创建型模式中的单例模式先来讲解,因为单例模式是最简单也是最容易理解的设计模式,上手快,易使用的设计模式。本文将从下面的流程来讲解

单例模式,后面讲述的设计模式也将使用这样的方式。

1、什么是单例模式?

2、单例模式的应用场景。

3、举例说明单例模式的使用。

4、总结单例模式的用法。

三、本文大纲

a、开篇。

b、摘要。

c、本文大纲。

d、单例模式的简介。

e、相关应用场景分析。

f、本文总结。

g、系列进度。

h、下篇预告。

四、单例模式的简介

本章我们将来讲述下单例模式的使用,首先我们来看看单例模式的定义:

单例模式:是一种软件设计中常用的设计模式,主要是用来控制某个类必须在某个应用程序中只能有一个实例存在。

有时候我们需要确保整个系统中只有某个类的一个实例存在,这样有利于我们协调控制系统的行为。例如:我们在某个系统中使用了发送短信的这样的服务,那么

我们可能希望通过单一的短信服务类的实例,而不是多个对象实例完成短信的发送服务。这时我们可以通过单例模式来完成。

image 上图简单描述了单例模式应用的位置。

我们看看单例模式的几种实现方式:

image

下面我们来举例说明下这2种方式的实现。

1、外部控制的方式

    public class Instance 
    { 
        private List<SendMessage> lists = new List<SendMessage>(); 
        private SendMessage sendInstance; 

        public SendMessage SInstance 
        { 
            get 
            { 
                return sendInstance; 
            } 
        } 

        public void InstanceMethod() 
        { 
            if (lists.Count == 0) 
            { 
                sendInstance = new SendMessage(); 
                lists.Add(sendInstance); 
            } 
            else 
            { 
                sendInstance = lists[0]; 
            } 
        } 
    }

2、内部控制方式

     public class Instance1 
     { 
        private static SendMessage sendInstance; 
        private static object _lock = new object(); 

        protected Instance1() 
        { 
        } 

        public static SendMessage SInstance 
        { 
            get 
            { 
                lock (_lock) 
                { 
                    if (sendInstance == null) 
                        sendInstance = new SendMessage(); 
                    return sendInstance; 
                } 
            } 
        } 
    }

这里有几点需要注意的地方,对于第二种方式有几个地方需要说明下,首先是要控制全局只有一个实例的类,请定义成静态实例,这样可以确保只有一个实例对

象,其次,这个对象的构造函数请声明成保护类型的成员,这样可以屏蔽通过直接实例化的形式来访问。通过这样的形式,客户可以不需要知道某个单例实例对象的内

部实现细节。一般情况下满足上面的2点需求就可以完成全局唯一访问入口的控制。当然可能在多线程的情况下采用这样的形式还会有一定的弊端,当然我们这里也简单

的讲解下相应的控制方案。方案如下:

    public class CoolInstance 
    { 
        private CoolInstance() 
        { 
        } 
        public static readonly CoolInstance Instance = new CoolInstance(); 
    }

看吧很简单吧,当然我们这里来简单解释下原理:

1、我们先把构造函数声明为私有的构造函数,这样我们能够屏蔽外部通过实例化的形式访问内部的成员函数。所有的成员函数的访问必须通过静态成员Instance

来完成访问。

2、这段代码通过定义公共、静态、只读的成员相当于在类被第一次使用时执行构造,由于是只读的,所以一旦构造后不允许修改,就不用担心不安全的问题。

相信对上面的介绍大家应该基本上知道单例模式的应用了,那么下面我们来看看项目中的实际应用场景及用法。

五、相关应用场景讲解

1、场景短信及邮件发送服务

那么我们将采用上面介绍的最“COOL”的方式来进行控制,提供发送短信及发送邮件的服务。

    public class CoolInstance 
    { 
        private CoolInstance() 
        { 
        } 

        public static readonly CoolInstance Instance = new CoolInstance(); 

        /// <summary> 
        /// 发送手机短信 
        /// </summary> 
        public bool SendMessage(string telNumber,string content) 
        { 
            return true; 
        } 

        /// <summary> 
        /// 发送邮件 
        /// </summary> 
        /// <param name="content"></param> 
        /// <param name="toMail"></param> 
        public bool SendMail(string content,string toMail) 
        { 
            return true; 
        } 
    }

我们再来看看调用类中如何书写完成调用。例如我们有个订单类,当有人新下订单时,将给卖家发送短信提醒功能。

   /// <summary> 
   /// 订单业务 
  /// </summary> 
  public class Order 
  { 
      public int Save() 
      { 
          //先是将订单的相关信息生成, 
          this.InitOrderInfo(); 

          //执行订单的持久化方法 
          int count= this.Add(); 

          //发送短信 
          CoolInstance.Instance.SendMessage(string.Empty, string.Empty); 
          //发送邮件 
          CoolInstance.Instance.SendMail(string.Empty, string.Empty); 

          return count; 
      } 

      /// <summary> 
      /// 初始化订单信息 
      /// </summary> 
      private void InitOrderInfo() 
      { 
      } 

      /// <summary> 
      /// 新增订单信息 
      /// </summary> 
      /// <returns></returns> 
      private int Add() 
      { 
          return 0; 
      } 
  }

这样我们就完成了短信发送服务及邮件发送服务的控制。主要还是根据自己的业务需要。

2、例如我们现在提供一个系统日志服务或者打印或者扫描的服务,我们希望全局只有一个访问入口,那么我们就可以通过这样的单例模式来实现这样的需求。

   public class PrintHelper 
    { 
        #region 构造函数 
        private PrintHelper() 
        { 
        } 

        public static readonly PrintHelper Instance = new PrintHelper(); 
        #endregion 

        #region 打印服务 

        /// <summary> 
        /// 直接打印服务 
        /// </summary> 
        /// <returns></returns> 
        public bool Print() 
        { 
            return true; 
        } 

        /// <summary> 
        /// 打印预览 
        /// </summary> 
        /// <returns></returns> 
        public bool PrintPreview() 
        { 
            return true; 
        } 

        #endregion 
    }

具体的调用类我就不写相应的代码,都和上面的形式类同,下面我们讲解下可能更特殊的需求,有时候我们可能需要更新我们创建的唯一实例,这时我们如何控

制单例实例对象的更新呢,有时候可能我们有这样的需求。下面我们来看看如何实现这样的需求。

3、可更新单例对象的场景

首先我们先说下什么情况下会遇到这样的更新方式呢?例如我们想在单例模式的类的构造函数是带有一定参数的情形时:

   public class UpdateHelper 
   { 
       private string type = string.Empty; 
       private static object _lock = new object(); 
       private static UpdateHelper instance; 
       private UpdateHelper(string valueType) 
       { 
           type = valueType; 
       } 

       public static UpdateHelper Instance 
       { 
           get 
           { 
               lock (_lock) 
               { 
                   if (instance == null) 
                   { 
                       //如果这里有多个条件需求的话,可能写起来会比较复杂,那么有更好的方式来处理吗? 
                       instance = new UpdateHelper("test!"); 
                   } 

                   return instance; 
               } 
           } 
       } 
   }

那么我们来分析几种办法,有没有更好的办法来处理呢?

1、首先我们不能手动实例化,所以我们没有办法动态传入构造函数参数,只能在类的内部指定这个参数,但是有时候我们需要动态的更新这个参数,那么这样的

形式显然就没有办法实现。

2、通过属性的方式,来动态的设置属性的内容来完成输出参数的改变,但是这样的方式可能太过自由,无法满足单例模式的初衷。

3、接口方式,因为接口必须要靠类来实现,所以更不靠谱,可以不考虑这样的方式。

4、通过Attribute的方式来将信息动态的注入到构造函数中,但是怎么说这样的方式是不是太兴师动众了呢?毕竟单例模式本来就是很简单的。

5、通过配置文件,通过config文件配置节点的形式来动态的配置相关信息,实现更新实例对象内容的情况。

通过上面的5种情况的分析,那么通过2、4、5可以实现这个要求,但是对比相应的代价来说,5的方式是最灵活也是最符合单例模式本来的规范要求,相对来说

成本和代价也可以接收。

<?xml version="1.0" encoding="utf-8" ?> 
<configuration> 
  <system.Web> 
    <add key="ssss" >value</add> 

  </system.Web> 
</configuration>

那么我们上面的单力模型中的代码只需要稍微的变化下即可,请看如下代码:

    public class UpdateHelper 
    { 
        private string type = string.Empty; 
        private static object _lock = new object(); 
        private static UpdateHelper instance; 
        private UpdateHelper(string valueType) 
        { 
            type = valueType; 
        } 

        public static UpdateHelper Instance 
        { 
            get 
            { 
                lock (_lock) 
                { 
                    if (instance == null) 
                    { 
                        //如果这里有多个条件需求的话,可能写起来会比较复杂,那么有更好的方式来处理吗? 
                        instance = new UpdateHelper(System.Configuration.ConfigurationManager.AppSettings["ssss"].ToString()); 
                    } 

                    return instance; 
                } 
            } 
        } 
    }

我想到这里大家都对单例模式有个简单的认识了,本文的内容就讲到这里。我们来回顾下我们讲述的内容:

image

六、本文总结

本文主要讲述了创建型模式中的单例模式,单例模式主要是用来控制系统中的某个类的实例的数量及全局的访问入口点。我们主要讲述了实现单例模式的方式,

分为外部方式及内部方式,当然我们现在采用的方式都是内部方式,还讲述了线程安全的单例模式及带有参数的构造函数的情况,根据配置文件来实现参数值的动态配

置的情况。希望本文的讲解能对不熟悉设计模式的同仁能够了解知道单例模式的应用,而对已熟知单例模式的同仁可以温故而知新,我会努力写好这个系列,当然我这

里可能在大牛的面前可能是班门弄斧吧,不过我会继续努力,争取写出让大家一看就明白的设计模式系列。本文错误之处再所难免,还请大家批评之处,我会继续改

进。

七、系列进度

创建型

1、系统架构技能之设计模式-单件模式

2、系统架构技能之设计模式-工厂模式

3、系统架构技能之设计模式-抽象工厂模式

4、系统架构技能之设计模式-创建者模式

5、系统架构技能之设计模式-原型模式

结构型

1、系统架构技能之设计模式-组合模式

2、系统架构技能之设计模式-外观模式

3、系统架构技能之设计模式-适配器模式

4、系统架构技能之设计模式-桥模式

5、系统架构技能之设计模式-装饰模式

6、系统架构技能之设计模式-享元模式

7、系统架构技能之设计模式-代理模式

行为型

1、系统架构技能之设计模式-命令模式

2、系统架构技能之设计模式-观察者模式

3、系统架构技能之设计模式-策略模式

4、系统架构技能之设计模式-职责模式

5、系统架构技能之设计模式-模板模式

6、系统架构技能之设计模式-中介者模式

7、系统架构技能之设计模式-解释器模式

八、下篇预告

下篇我们将会介绍我们大家最熟知的工程模式,当然我会更多的结合实例来讲解每个设计模式的应用场景及具体的实例,来更清晰的描述什么情况下用什么模

式,及每个模式之间的区别。大家的支持就是我书写的动力,希望大家多多支持我吧!

转自:https://www.cnblogs.com/hegezhou_hot/archive/2010/10/02/1841390.html

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

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

相关文章

MR混合现实汽车维修情景实训教学演示

MR混合现实技术应用于汽车维修课堂中&#xff0c;能够赋予学生更加真实&#xff0c;逼真地学习环境&#xff0c;让学生在情景体验中不断提高自己的专业能力。 MR混合现实汽车维修情景实训教学演示具体体现在&#xff1a; 1. 虚拟维修指导&#xff1a;利用MR技术&#xff0c;可…

绘图系统二:多图绘制系统

文章目录 坐标轴控件坐标系控件绘制多组数据源代码 本文基于&#xff1a;&#x1f4c8;从0开始实现一个三维绘图系统 坐标轴控件 三个坐标轴xyz从外观上看其实毫无区别&#xff0c;这种标签和输入框的组合十分常见&#xff0c;为了便于调用&#xff0c;最好实现一个类。 tki…

高级时钟项目

高级时钟项目 笔者来介绍一下一个简单的时钟项目&#xff0c;主要功能就是显示时间 1、背景 2、数码管版本&#xff08;第一版&#xff09; 3、OLED屏幕版本&#xff08;第二版&#xff09; 3.1、Boot 3.2、app 3.3、上位机 界面一&#xff1a;时间天气显示 界面二 &…

centos7部署时间同步(ntp)服务器

centos7部署时间同步&#xff08;ntp&#xff09;服务器 这里搭建ntp服务器&#xff0c;服务端和客户端&#xff0c;客户端去拉取服务端的时间&#xff0c;为自己所用。 小白教程&#xff0c;一看就会&#xff0c;一做就成。 1.服务端搭建 a.安装ntp yum -y install ntp* …

【广州华锐互动】数字孪生智慧楼宇3D可视化系统:掌握实时运行状态,优化运营管理

在过去的几年中&#xff0c;科技的发展极大地改变了我们的生活和工作方式。其中&#xff0c;三维数据可视化技术的出现&#xff0c;为我们提供了全新的理解和观察世界的方式。特别是在建筑行业&#xff0c;数字孪生智慧楼宇3D可视化系统的出现&#xff0c;让我们有机会重新定义…

1.9 动态解密ShellCode反弹

动态解密执行技术可以对抗杀软的磁盘特征查杀。其原理是将程序代码段中的代码进行加密&#xff0c;然后将加密后的代码回写到原始位置。当程序运行时&#xff0c;将动态解密加密代码&#xff0c;并将解密后的代码回写到原始位置&#xff0c;从而实现内存加载。这种技术可以有效…

“金九”行情如期而至?六大券商看市

八月最后一个交易日&#xff0c;股指小幅低开震荡后逐波下行&#xff0c;成交量有所萎缩。市场仍处于对管理层组合政策的消化过程之中。热点主要集中在芯片领域。 展望九月&#xff0c;机构认为&#xff0c;当前市场已处于底部阶段&#xff0c;对于基本面及风险的悲观定价已经…

原型链的终点为什么是null?

一般来说,大家讲到原型链的时候到最后都会说: 所有的对象都是由Object构造函数所构造的,所以原型链的终点是Object.prototype. 而看过原型链完整图的应该都有印象,实际上真要讲终点的 话,其实原型链的终点是-null ! ! ! 于是我们思考下面这个问题 那这样是不是就陷入了死…

前端实现动态路由(前端控制全部路由,后端返回用户角色)

优缺点 优点&#xff1a; 不用后端帮助&#xff0c;路由表维护在前端逻辑相对比较简单&#xff0c;比较容易上手权限少的系统用前端鉴权更加方便 缺点&#xff1a; 线上版本每次修改权限页面,都需要重新打包项目大型项目不适用如果需要在页面中增加角色并且控制可以访问的页…

pdf怎么转换成word?

随着数字化时代的到来&#xff0c;PDF(Portable Document Format)已成为最受欢迎的文档格式之一&#xff0c;因其在各种设备上的可视性和稳定性而备受推崇。然而在某些情况下&#xff0c;将PDF转换为Word文档可能是必要的&#xff0c;这使得编辑、修改和重新格式化文本变得更加…

在window上安装hadoop3.3.4

暑假不知道啥原因电脑死机啦。环境需要重新配一下 首先需要配置Hadoop集群&#xff0c;但是为了代码调试方便需要先在Windows上配置Hadoop环境。 1.前期准备 首先在搭建Hadoop环境之前需要先安装JDK&#xff0c;并且配置好Java环境变量。 这里有个bug就是Java环境变量中不允许…

2023秋招得物面经 8.31总结

1.数据结构中有哪些树 在数据结构中&#xff0c;常见的树包括&#xff1a; 二叉树&#xff08;Binary Tree&#xff09;&#xff1a;每个节点最多有两个子节点&#xff0c;用于表示有层次关系的数据结构&#xff0c;如二叉搜索树、堆等。 二叉搜索树&#xff08;Binary Searc…

jmeter调试错误大全

一、前言 在使用jmeter做接口测试的过程中大家是不是经常会遇到很多问题&#xff0c;但是无从下手&#xff0c;不知道从哪里开始找起&#xff0c;对于初学者而言这是一个非常头痛的事情。这里结合笔者的经验&#xff0c;总结出以下方法。 二、通过查看运行日志调试问题 写好…

虚拟机Linux20.04磁盘扩展

扩展之前必须要确保&#xff01;没有快照&#xff01; ps:先把快照删掉&#xff0c;如果担心弄坏的话可以先克隆一个 如果不删的话就会跟下面一样无法点击扩展&#xff1a; 删除了快照之后就可以点击这个【扩展】&#xff0c;输入你要的磁盘大小即可。 &#xff08;我这里原…

智能感测棒形静电消除器所具备的特点

智能感测棒形静电消除器是一种具有联网监控功能的设备。它可以通过内置的传感器实时感知周围的静电情况&#xff0c;并采取相应的措施进行消除。 该设备通过联网功能&#xff0c;可以将感测到的静电信息传输到指定的监控平台或手机应用程序中进行实时监控与管理。用户可以随时…

Win11搭建 Elasticsearch 7 集群(一)

一&#xff1a; ES与JDK版本匹配一览表 elasticsearch从7.0开始默认安装了java运行环境&#xff0c;以便在没有安装java运行环境的机器上运行。如果配置了环境变量JAVA_HOME&#xff0c;则elasticsearh启动时会使用JAVA_HOME作为java路径&#xff0c;否则使用elasticsearch根目…

防溺水广播警示系统 python

防溺水广播警示系统通过pythonyolo系列网络框架模型算法&#xff0c;防溺水广播警示系统以识别和判断危险水域中是否有人员溺水的情况。一旦出现溺水现象&#xff0c;算法将立即发出警报信号&#xff0c;并自动启动广播系统进行警示。Python是一种由Guido van Rossum开发的通用…

[递归] 子集 全排列和组合问题

1.1 子集I 思路可以简单概括为 二叉树&#xff0c;每一次分叉要么选择一个元素&#xff0c;要么选择空&#xff0c;总共有n次&#xff0c;因此到n1进行保存结果&#xff0c;返回。像这样&#xff1a; #include <cstdio> #include <vector> #include <algorithm&…

[ros][ubuntu]ros在ubuntu18.04上工作空间创建和发布一个话题

构建catkin工作空间 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src catkin_init_workspace cd ~/catkin_ws/ catkin_make 配置环境变量 echo "source ~/catkin_ws/devel/setup.bash" >> ~/.bashrc source ~/.bashrc 检查环境变量 echo $ROS_PACKAGE_PATH…

学习笔记-ThreadLocal

ThreadLocal 什么是ThreadLocal&#xff1f; ThreadLocal 是线程本地变量类&#xff0c;在多线程并行执行过程中&#xff0c;将变量存储在ThreadLocal中&#xff0c;每个线程中都有独立的变量&#xff0c;因此不会出现线程安全问题。 应用举例 解决线程安全问题&#xff1a;例…