程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 一步一步造個Ioc輪子(二),詳解泛型工廠,ioc輪子

一步一步造個Ioc輪子(二),詳解泛型工廠,ioc輪子

編輯:C#入門知識

一步一步造個Ioc輪子(二),詳解泛型工廠,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的可以直接復制代碼出來

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved