代码改变世界

一步一步造个IoC轮子(二),详解泛型工厂

2016-06-29 20:56  BlackMagic#  阅读(3790)  评论(5编辑  收藏  举报

一步一步造个Ioc轮子目录

一步一步造个IoC轮子(一):Ioc是什么
一步一步造个IoC轮子(二):详解泛型工厂
一步一步造个IoC轮子(三):构造基本的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的可以直接复制代码出来