zoukankan      html  css  js  c++  java
  • 阅读源码学设计模式-单例模式

    有些编码套路是公认的,大家都参照其编写符合可观赏性的代码,那就是设计模式

    现在.NETcore 默认提供了DI功能,那我想设计一个全局的引擎类,进行注入服务、解析服务、配置中间件。并且要求该引擎类全局唯一,其他地方不能进行实例化。那单例模式就派上用场了。 单例模式官方定义:

    确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类成为单例类,它提供全局访问的方法。

    伪代码实现需求

     public class AAEngine
        {
            private static AAEngine aAEngine = null;
            private AAEngine() { }
            //获取实例
            public static AAEngine GetInstance()
            {
                if (aAEngine == null)
                {
                    aAEngine = new AAEngine();
                }
                return aAEngine;
            }
            //添加服务到容器
            public  void ConfigureService()
            {
                Console.WriteLine("添加服务到容器");
            }
            //添加中间件到请求管道
            public  void ConfigureRequestPipeline()
            {
                Console.WriteLine("添加中间件到请求管道");
            }
            //解析服务
            public  void Resolve<T>() where T : class
            {
                Console.WriteLine("解析服务");
            }
    
        }

    在Main函数中调用

     //单例模式
            static void runSingleton()
            {
                var aAEngine = AAEngine.GetInstance();
                aAEngine.ConfigureService();
                aAEngine.ConfigureRequestPipeline();
                Console.WriteLine("Oh yeah 单例模式! ");
            }

    输出

    小结:从实例代码中我们看到构造函数设置了级别为private,这样可以防止外部进行new实例化,外部可以通过GetInstance方法获取实例对象。实例代码其实是有点瑕疵的在多线程的情况下,会违背单例的初衷,我们下面进行如何解决这个问题。

    突然脑海中闪现出曾经的面试场景,饿汉式单例和懒汉式单例,或许对问题有所有帮助;

    饿汉式单例

    饿汉试单例是在类加载的时候就已经创建了对象。代码如下

    public class AAEngine1
        {
            private static AAEngine1 aAEngine = new AAEngine1();
            private AAEngine1() { }
            //获取实例
            public static AAEngine1 GetInstance()
            {
                return aAEngine;
            }
    
            //添加服务到容器
            public void ConfigureService()
            {
                Console.WriteLine("添加服务到容器");
            }
            //添加中间件到请求管道
            public void ConfigureRequestPipeline()
            {
                Console.WriteLine("添加中间件到请求管道");
            }
            //解析服务
            public void Resolve<T>() where T : class
            {
                Console.WriteLine("解析服务");
            }
        }

    小结:在类被加载时,静态变量aAEngine会被初始化,AAEngine1类的唯一实例将会创建,则多线程并发的场景下,也可确保单例对象的唯一性; 那什么是懒汉式单例呢?其实最上面的AAEngine就是懒汉式单例,在多线程并发的场景下懒汉式单例有问题,如何解决 答案是通过锁的方式。

    懒汉式单例+线程锁

    懒汉式单例有延迟Lazy的思想,只有在需要的时候才去加载实例。在多线程并发的场景下我们使用双重检查锁定(Double-Check Locking)。完成代码如下:

      private static AAEngine aAEngine = null;
            private static object lockObj=new object();
            private AAEngine() { }
            //获取实例
            public static AAEngine GetInstance()
            {
                //第一重验证
                if (aAEngine == null)
                {
                    lock (lockObj) 
                    {
                        //第二重验证
                        if (aAEngine==null) 
                        {
                            aAEngine = new AAEngine();
                        }
                    }
                }
                return aAEngine;
            }

    单例模式在开源Nop项目中实践

    为了配合你没有阅读过Nop项目源码,我会把涉及到单例的几个类源码贴出来。主要设计到3个类Singleton、IEngine、EngineContext。

    Singleton类
       public class Singleton<T> : BaseSingleton
        {
            private static T instance;
    
            /// <summary>
            /// The singleton instance for the specified type T. Only one instance (at the time) of this object for each type of T.
            /// </summary>
            public static T Instance
            {
                get => instance;
                set
                {
                    instance = value;
                    AllSingletons[typeof(T)] = value;
                }
            }
        }
    IEngine类
    public interface IEngine
        {
            /// <summary>
            /// 添加配置服务 Add and configure services
            /// </summary>
            /// <param name="services">Collection of service descriptors</param>
            /// <param name="configuration">Configuration of the application</param>
            /// <param name="nopConfig">Nop configuration parameters</param>
            /// <returns>Service provider</returns>
            IServiceProvider ConfigureServices(IServiceCollection services, IConfiguration configuration, NopConfig nopConfig);
    
            /// <summary>
            /// 配置请求管道 Configure HTTP request pipeline
            /// </summary>
            /// <param name="application">Builder for configuring an application's request pipeline</param>
            void ConfigureRequestPipeline(IApplicationBuilder application);
    
            /// <summary>
            /// 解析服务 Resolve dependency
            /// </summary>
            /// <typeparam name="T">Type of resolved service</typeparam>
            /// <returns>Resolved service</returns>
            T Resolve<T>() where T : class;
    
            /// <summary>
            /// 解析服务 Resolve dependency
            /// </summary>
            /// <param name="type">Type of resolved service</param>
            /// <returns>Resolved service</returns>
            object Resolve(Type type);
    
            /// <summary>
            ///  解析所有服务Resolve dependencies
            /// </summary>
            /// <typeparam name="T">Type of resolved services</typeparam>
            /// <returns>Collection of resolved services</returns>
            IEnumerable<T> ResolveAll<T>();
    
            /// <summary>
            /// Resolve unregistered service
            /// </summary>
            /// <param name="type">Type of service</param>
            /// <returns>Resolved service</returns>
            object ResolveUnregistered(Type type);
        }
    EngineContext 引擎上下文类
      #region Methods
    
            /// <summary>
            /// Create a static instance of the Nop engine.
            /// </summary>
            [MethodImpl(MethodImplOptions.Synchronized)]
            public static IEngine Create()
            {
                //create NopEngine as engine
                return Singleton<IEngine>.Instance ?? (Singleton<IEngine>.Instance = new NopEngine());
            }
    
            /// <summary>
            /// Sets the static engine instance to the supplied engine. Use this method to supply your own engine implementation.
            /// </summary>
            /// <param name="engine">The engine to use.</param>
            /// <remarks>Only use this method if you know what you're doing.</remarks>
            public static void Replace(IEngine engine)
            {
                Singleton<IEngine>.Instance = engine;
            }
            
            #endregion
    
            #region Properties
    
            /// <summary>
            /// Gets the singleton Nop engine used to access Nop services.
            /// </summary>
            public static IEngine Current
            {
                get
                {
                    if (Singleton<IEngine>.Instance == null)
                    {
                        Create();
                    }
    
                    return Singleton<IEngine>.Instance;
                }
            }
    
            #endregion
        }

    从nop的源码中我们发现,他使用的懒汉式单例(含双重检查锁定),外部访问IEngine实例是通过EngineContext上下文来访问的。在创建IEngine实例方法create()时,使用时的 [MethodImpl(MethodImplOptions.Synchronized)]特性,表示create方法只能由一个线程执行,类似lock锁。

    如何使用,代码如下

     //create engine and configure service provider
     var engine = EngineContext.Create();
     var serviceProvider = engine.ConfigureServices(services, configuration, nopConfig);
      。。。。。。。。。。。。
     EngineContext.Current.Resolve<IScheduleTaskService>();

    nop封装的优秀的代码,也收录到我的开源项目中了,喜欢可以star下 https://github.com/ChengLab/AAFrameWork

  • 相关阅读:
    CAD图形引擎库VectorDraw
    基于COM的矢量图像控件VectorDraw
    [转]电子表格Spread 7.0线上发布会
    ProEssentials图表教程汇总
    几种JS 引擎介绍(不同浏览器有不同的引擎)
    我的 学习链接
    ExtJS 2.3版 源代码的解析(转)
    javascript调试原理(一)(转)
    使用GoogleCode SVN服务
    javascript调试原理(二) 模拟实现 (转)
  • 原文地址:https://www.cnblogs.com/chengtian/p/11856328.html
Copyright © 2011-2022 走看看