zoukankan      html  css  js  c++  java
  • 设计模式单例模式

    定义


    单例模式,属于创建类型的一种常用的设计模式。它的目的就是为了创建的类在当前进程中只有一个实例

    目的


    从定义可以看出,使用单例模式的目的无非就是下面两个:

    • 全局唯一
    • 全局共享

    优点


    • 确保全局共享同一个实例
    • 节约系统资源

    实现手段


    1. 静态类

    这种方式不是单例模式,但可以满足需求,在正式生产中也会经常用到。

    代码

    public static class SingletonSample1  
    {  
        private static int _counter = 0;
        
        public static int IncreaseCount()  
        {  
            return ++_counter;  
        }
    }
    
    

    注意:这里的++_counter其实存在高并发问题,严格上应该用Interlocked.Increment(ref _counter)的方式,由于我们主要讲的是单例模式并且简单且能演示效果,所以故意忽略了这一点。下同

    优点

    • 使用起来方便,简单

    缺点

    • 静态类不能继承类,也不能实现接口,不能通过接口或者抽象方法(虚方法)实现多态;
    • 静态类必须在第一次加载时初始化,如果项目中用不到会导致资源浪费;

    2. 单例模式一

    这是最简单的一种单例模式,也是比较常用的一种方式,可在正式生产中使用。

    代码

    public sealed class SingletonSample2  
    {  
        private static readonly SingletonSample2 _instance = new SingletonSample2();
    
        private int _counter = 0;
    
        private SingletonSample2() { }
    
        public static SingletonSample2 Instance
        {
            get
            {
                return _instance;
            }
        }
    
        public int IncreaseCount()
        {
            return ++_counter;
        }
    }
    
    

    优点

    • 解决了静态类不能继承类,不能实现接口,不能通过接口或者抽象方法(虚方法)实现多态的问题;

    缺点

    • 没有解决第一次加载时初始化,资源浪费的问题。

    以上两种方式都存在第一次加载时,资源浪费的问题,但在内存资源越来越便宜的今天,通常这种浪费是可以接受的,因此也不必过于纠结这种浪费。当然,在条件允许的情况下,能优化还是要优化的。

    3. 单例模式二

    该方式是改进过程中的过渡阶段,不可用于生产。

    代码

    public class SingletonSample3
    {
        private static SingletonSample3 _instance;
    
        private int _counter = 0;
    
        private SingletonSample3() { }
    
        public static SingletonSample3 Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new SingletonSample3();
                }
    
                return _instance;
            }
        }
    
        public int IncreaseCount()
        {
            return ++\_counter;
        }
    }
    
    

    优点

    • 解决了资源浪费的问题;

    缺点

    • 引入了高并发的新问题。

    4. 单例模式三

    该方式也是改进过程中的过渡阶段,不可用于生产。

    代码

    public class public class SingletonSample4
    {
        private static SingletonSample4 _instance;
        private static readonly object _locker = new object();
        private int _counter = 0;
    
        private SingletonSample4() { }
    
        public static SingletonSample4 Instance
        {
            get
            {
                lock (_locker)  
                {
                    if (_instance == null)
                    {
                        _instance = new SingletonSample4();
                    }
    
                    return _instance;
                }
            }
        }
    
        public int IncreaseCount()
        {
            return ++_counter;
        }
    }
    
    

    注意:视频中讲到这里时,我其中有提到热启动关键词,我把系统预热口误说成了热启动,由于这两个概念之间有较大的差别,所以这里纠正一下。

    优点

    • 解决了高并发问题;

    缺点

    • 引入了性能问题。

    5. 单例模式四

    著名的双检锁模式,完美解决问题,可用于生产。

    代码

    public class SingletonSample5
    {
        private static volatile SingletonSample5 _instance;
        private static readonly object _locker = new object();
        private int _counter = 0;
    
        private SingletonSample5() { }
    
        public static SingletonSample5 Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_locker)
                    {
                        if (_instance == null)
                        {
                            _instance = new SingletonSample5();
                        }
                    }
                }
    
                return _instance;
            }
        }
    
        public int IncreaseCount()
        {
            return ++_counter;
        }
    }
    

    注意:volatile是必须的,因为它可以保证new不会被指令重排序,详细可看视频部分的分析。

    优点

    • 解决了上述实现方式的各种设计缺陷;

    缺点

    • 代码有点复杂。

    6. 单例模式五

    .Net支持的一种优雅版本的实现方式,前面讲了那么多其实就是为了引出该方式,强烈建议使用该版本

    代码

    public class SingletonSample6
    {
        private static readonly Lazy<SingletonSample6> _instance  
            = new Lazy<SingletonSample6>(() => new SingletonSample6());
    
        private int _counter = 0;
    
        private SingletonSample6() { }
    
        public static SingletonSample6 Instance
        {
            get
            {
                return _instance.Value;
            }
        }
    
        public int IncreaseCount()
        {
            return ++_counter;
        }
    }
    

    优点

    • 代码优雅简洁同时满足需求

    缺点

    • 当系统中有大量单例模式时,会有较多重复代码

    7. 单例模式六

    泛型版本,是否使用视情况而定。

    代码

    public class SingletonSampleBase<TSingleton> where TSingleton : class
    {
        private static readonly Lazy<TSingleton> _instance  
            = new Lazy<TSingleton>(() => (TSingleton)Activator.CreateInstance(typeof(TSingleton), true));
    
        protected SingletonSampleBase() { }
    
        public static TSingleton Instance
        {
            get
            {
                return _instance.Value;
            }
        }
    }
    
    public class SingletonSample7 : SingletonSampleBase<SingletonSample7>
    {
        private int _counter = 0;
    
        private SingletonSample7() { }
    
        public int IncreaseCount()
        {
            return ++_counter;
        }
    }
    

    优点

    • 封装了重复代码

    缺点

    • 违反了依赖倒置原则(虽然在父类中是通过反射创建的子类,但本质还是在父类中创建了子类)

    总结


    • 单例模式还可通过IOC容器实现,视频中在讲到IOC容器是也发生了多次口误,将注册说成了注入,这里也纠正一下。
    • 最后举的一个用单例模式实现SqlHelper的例子,重点是为了突出相对于静态类,实例类在多态扩展方面的优势,其实如果没有类似这种扩展需求,静态类就足以应付绝大多数的需求。

    单例模式实现方式如此之多,但实际上大多数情况需要使用单例的时候都可以用静态类实现,比如一些工具类,而其他场景直接用单例模式五或者单例模式六即可,著名的双检索其实也是大可不必的,毕竟跟单例模式五相比,体现不出任何优势,还更容易出错。

    源码链接

  • 相关阅读:
    .net core 学习小结之 配置介绍(config)以及热更新
    .net core 学习小结之环境配置篇
    powershell下载网站图片
    Powershell 脚本输出前十条消耗内存的进程到excel
    Linux 自学shell
    使用bat脚本进行开机启动批处理
    Git 创建分支并合并主分支
    Git 的使用及其一些基本用法
    点击按钮复制文本到剪切板
    关于一些基本排序的实现
  • 原文地址:https://www.cnblogs.com/FindTheWay/p/13559259.html
Copyright © 2011-2022 走看看