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