你的位置:首页 > ASP.net教程

[ASP.net教程]一步一步造个Ioc轮子(二),详解泛型工厂


一步一步造个Ioc轮子目录

.net core发布了,一步一步造个Ioc轮子,弄点.net魔法,近new的速度(一)一步一步造个Ioc轮子(二),详解泛型工厂

详解泛型工厂

既然我说Ioc容器就是一个豪华版工厂,自动化装配的工厂,那我们就从工厂入手吧,先造个工厂,然后升级成Ioc容器

首先我们来写一个最最最简单的抽象工厂类,还是以前一篇的短信为例

  public class SMSFactory  {    public static ISMS Get()    {      return new XSMS();    }  }

然后我们琢磨着怎么把这个XSMS不要写死在代码上,嗯加一个注册方法,把SMS对象传进去

  public class SMSFactory  {    static ISMS _sms;    public static ISMS Get()    {      return _sms;    }    public static void Reg(ISMS sms)    {      _sms = sms;    }  }

这个代码分离了业务对XSMS的依赖,但依然要在程序启动时注册一个ISMS实现对象进去如:SMSFactory.Reg(new XSMS());

我们再琢磨着这个工厂越写越复杂,还只能用来做短信,能不能搞成通用呢,嗯,改成泛型吧

  public class Factory<T> where T:class  {    static T _obj;    public static T Get()    {      return _obj;    }    public static void Reg(T obj)    {      _obj = obj;    }  }

嗯,搞出一个好简单的泛型工厂了,我们再琢磨一下,这东西要在系统启动就传new好的对象进去,有点不科学啊,能不能让工厂自己new 呢,试试吧

  public class Factory<T> where T:class,new()  {    public static T Get()    {      return new S(); //晕了,S的从哪里取呢    }    public static void Reg<S>() where S:T    {      //怎么把S(继承类)保存下来呢???这下头痛了    }  }

貌似不行哦,我们怎么保存继承类的信息在这个工厂里呢,聪明的前人想到了一个方法,用一个含S信息的对象创建不就行了,这个对象又继承了一个含Create方法的接口,Get的时候调用这个对象的Create的方法

  public class Factory<T> where T : class  {    static ICreate creater;    interface ICreate    {      T Create();    }    class Creater<U> : ICreate where U : T, new()    {      public T Create()      {        return new U();      }    }    public static T Get()    {      //调用creater对象的Create方法实际上相当于调用了Creater<U>的new U()      return creater.Create();    }    public static void Reg<S>() where S : T, new()    {      //在这里,我们把S的信息保存到了creater对象上      creater = new Creater<S>();    }  }

完美啊,用一个临时的对象保存了继承类的信息,这样就可以在工厂类注册继承类进去了,回到上篇小黄的改来改去的SMS模块问题,我们也可以用这个泛型工厂解决掉业务的依赖问题了var sms = Factory<ISMS>.Get();只是要在启动的配置里注册上实现类

我们能不能再扩展一下,让他支持单例呢,答案当然是yes了,只要对Creater改造一下

  public class Factory<T> where T : class  {    static ICreate creater;    interface ICreate    {      T Create();    }    class Creater<U> : ICreate where U : T, new()    {      public T Create()      {        return new U();      }    }    class SingletonCreater<U> : ICreate where U : T, new()    {      T instance;      object locker = new object();      public T Create()      {        //使用双检锁        if (instance == null)        {          lock (locker)          {            if (instance == null)            {              Interlocked.Exchange(ref instance, new U());            }          }        }        return instance;      }    }    public static T Get()    {      return creater.Create();    }    public static void Reg<S>() where S : T, new()    {      creater = new Creater<S>();    }    public static void RegSingleton<S>() where S : T, new()    {      creater = new SingletonCreater<S>();    }  }

哟,真行,不过有锁,能不能去掉锁呢,yes,我们来用静态readonly魔法,创建一个内部类,只有访问这个内部类时这个对象才会被创建,而且是线程安全的

  public class Factory<T> where T : class  {    static ICreate creater;    interface ICreate    {      T Create();    }    class Creater<U> : ICreate where U : T, new()    {      public T Create()      {        return new U();      }    }    class SingletonCreater<U> : ICreate where U : T, new()    {      class InstanceClass      {        public static readonly T Instance = new U();      }      public T Create()      {        return InstanceClass.Instance;      }    }    public static T Get()    {      return creater.Create();    }    public static void Reg<S>() where S : T, new()    {      creater = new Creater<S>();    }    public static void RegSingleton<S>() where S : T, new()    {      creater = new SingletonCreater<S>();    }  }

果然黑魔法,接下来我们再魔改一下这个泛型工厂,让他支持传入参数,其实也很简单,用个字典保存一下key和creater的对应关系就是了

  public class Factory<T> where T : class  {    interface ICreate    {      T Create();    }    class Creater<U> : ICreate where U : T, new()    {      public T Create()      {        return new U();      }    }    class SingletonCreater<U> : ICreate where U : T, new()    {      class InstanceClass      {        public static readonly T Instance = new U();      }      public T Create()      {        return InstanceClass.Instance;      }    }    #region 无参数的    static ICreate creater;    public static T Get()    {      return creater.Create();    }    public static void Reg<S>() where S : T, new()    {      creater = new Creater<S>();    }    public static void RegSingleton<S>() where S : T, new()    {      creater = new SingletonCreater<S>();    }    #endregion    #region 有参数的    static IDictionary<string, ICreate> creaters = new System.Collections.Concurrent.ConcurrentDictionary<string, ICreate>();    public static T Get(string key)    {      ICreate ct;      if (creaters.TryGetValue(key, out ct))        return ct.Create();      throw new Exception("未注册");    }    public static void Reg<S>(string key) where S : T, new()    {      creaters[key] = new Creater<S>();    }    public static void RegSingleton<S>(string key) where S : T, new()    {      creaters[key] = new SingletonCreater<S>();    }    #endregion  }

好了,泛型工厂详解和魔改完毕,支持注册单例,测试一下,完美,是不是已经有了Ioc的味道了,下一步我们就再魔改这个工厂,改造为可以从配置读取及优化从参数的获取的性能

 


 

我不是想引起战争,但真泛型确是.net的魔法,java这样搞是不行的,java只能反射了,接近new的性能就是.net真泛型所赋予的

 

代码下载,用VS2015 update3写的,不用.net core的可以直接复制代码出来