夸克之书

  • 首页
  • 科普
  • 笔记
  • .NET/C#
  • 物联网
  • 算法
  • Linux
  • 树莓派
夸克之内,别有洞天
  1. 首页
  2. .NET/C#
  3. 正文

23种常见的设计模式(1):单例模式

2019-12-29 5024点热度 0人点赞 0条评论

首先一个问题,什么是单例模式?根据我的理解,单例模式重点突出一个“单”字,无论干什么都是在同一个类中进行的,一个类只能生成一个对象,所有的操作都通过这个对象来进行操作。

很显然,要控制一个类只能提供一个单一的对象供外部调用使用,就需要将构造行数私有化,让其他人(类)不能创建我这个类。同时,要对外提供服务,我就需要将这个已经由我创建好了的对象公布出去,让别的人(类)通过我公布的实例来对我进行访问。

以一个Log类作为例子,在项目开发的生命周期中,需要在无数的地方打印日志。不可能每次调用一下日志打印就要去创建一个日志打印对象,这样多浪费性能。而通过单例模式创建日志对象后,每次访问日志打印方法时,获取创建好了的单例即可。

整个日志类代码如下:

public class Log
{
    public static Log _logger = null;

    /// <summary>
    /// 私有化构造函数
    /// </summary>
    private Log()
    {

    }

    /// <summary>
    /// 创建单例时的锁
    /// </summary>
    private static readonly object locker = new object();

    /// <summary>
    /// 提供对外的访问
    /// 懒汉式创建方式
    /// </summary>
    public static Log Instance
    {
        get
        {
            if(_logger == null)
            {
                lock (locker)
                {
                    if(_logger == null)
                    {
                        _logger = new Log();
                    }
                }
            }
            return _logger;
        }
    }

    /// <summary>
    /// 写日志的方法
    /// </summary>
    /// <param name="text"></param>
    public void Write(string text)
    {
        if (string.IsNullOrEmpty(text))
        {
            return;
        }
        Console.WriteLine($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}]{text}");
    }
}

写日志时,通过调用Write方法,打印日志即可:

static void Main(string[] args)
{
    Log.Instance.Write("这个是要打印的Log.");
    Console.ReadKey(false);
}

运行结果:

%title插图%num
demo运行结果

分析代码可以看到,在创建单例时,用了lock来保证线程安全。为什么要这样做呢?来看下下面这段非线程安全创建单例的代码:

public static Log Instance
{
    get
    {
        if (_logger == null)
        {
            _logger = new Log();
        }
        return _logger;
    }
}

当我们将这段代码应用在多线程高并发环境中时,如果两个线程同时调用Instance后,就能出现创建创建两次对象的情况(每个线程创建一遍)。为了避免出现这种不可预料的错误,就需要在创建单例对象的时候进行加锁,保证从始至终都是一个对象来提供服务。

那么单例模式有什么优点和缺点呢?

优点:

· 单例模式仅在内存中创建了一个实例,减小内存开支。当遇到一个对象需要频繁的进行创建和销毁,而创建和销毁时的性能又不能得到保障时,就可以通过创建单例来避免这种性能损失。
· 避免对资源的多重占用,例如以上的日志Demo,如果要写日志到文件的话,就可以避免对日志文件的多重占用(日志类仅作为一个demo)

缺点:

· 单例模式扩展非常困难,想要扩展,可能只有通过修改代码来进行扩展了。
· 单例模式与单一职责原则冲突,单例模式中一个类要进行的操作肯定不止一种。

分析完优缺点,再更具单例模式特性,很容易就能想到在哪里场景下用单例模式比较合适。比如:

· 生成唯一序列号的场景,单例模式可以保证生成的UUID是完全独立的。
· 需要优化重复IO读写的场景。
· 各种工具类(实际上我觉得各种工具类适合使用扩展方法)

题外话

其实单例也可以不单例,一个单例模式中虽然提供服务的是一个实例,但是一个实例的内部也可以多个对象的组合呀??!

本篇代码下载:点此下载

参考:

1、《设计模式之禅》 秦小波 著

本作品采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可
标签: 暂无
最后更新:2020-12-13

afirefish

这个人很懒,什么都没留下

打赏 点赞
< 上一篇
下一篇 >

文章评论

您需要 登录 之后才可以评论
放松一下
https://www.quarkbook.com/wp-content/uploads/2021/05/凤凰传奇-海底(Live).flac
分类
  • .NET/C#
  • Linux
  • 树莓派
  • 物联网
  • 科普
  • 笔记
  • 算法
  • 默认
最新 热点 随机
最新 热点 随机
Visual Studio 2026(VS2026) 密钥/激活码 在代码中判断龙芯新旧世界平台 Windows获取固定后缀的IPv6地址 目前为止,你可能找不到第二台支持志强的1L小主机(P350 Tiny+W-1350+ECC+双NVME+PCIE扩展)!!! iKuai(爱快)实现成都移动IPTV IPoE拨号 Linux EXT4分区误删除后数据恢复
Visual Studio 2026(VS2026) 密钥/激活码
23种常见的设计模式(2):工厂方法模式 在.NET 6中System.Drawing.Common引发的“The type initializer for 'Gdip' threw an exception.”异常 记一次成功的Update(Oracle误Delete或Update数据还原) .NET Core EF提示“Unable to cast object of type 'System.Boolean' to type 'System.Int16'.” 树莓派安装Golang环境 PVE使用山克UPS(ViewPower)
最近评论
xD 发布于 10 个月前(07月17日) 牛逼
Eagle 发布于 2 年前(10月21日) 参考博主教程成功搞定了成都移动IPTV组播转单播,电脑、手机都可以播放了。但目前有个问题,原IPTV...
rundoze 发布于 2 年前(08月31日) 牛逼
cc21216695 发布于 3 年前(09月27日) 试了一下,加入启动项也无效,压根没有用
afirefish 发布于 3 年前(11月28日) 非常感谢,非常棒!
书签
  • 打赏
  • 毒鸡汤
  • 米店
  • 金鱼直播间

COPYRIGHT © 2023 quarkbook.com. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

蜀ICP备15036129号-9

登录
注册|忘记密码?