乐趣区

关于设计模式:3-设计模式单例模式

定义


单例模式,属于创立类型的一种罕用的设计模式。它的目标就是为了创立的类在以后过程中 只有一个实例

目标


从定义能够看出,应用单例模式的目标无非就是上面两个:

  • 全局惟一
  • 全局共享

长处


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

实现伎俩


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 的例子,重点是为了突出绝对于动态类,实例类在多态扩大方面的劣势,其实如果没有相似这种扩大需要,动态类就足以应酬绝大多数的需要。

单例模式实现形式如此之多,但实际上大多数状况须要应用单例的时候都能够用动态类实现,比方一些工具类,而其余场景间接用 单例模式五 或者 单例模式六 即可,驰名的双检索其实也是大可不必的,毕竟跟 单例模式五 相比,体现不出任何劣势,还更容易出错。

视频分享链接
更多内容请关注公众号:

退出移动版