既然我說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的可以直接復制代碼出來