相比較傳統的工廠模式IFactory/Concrete Factory會反覆引用並編譯代碼
可是做爲開發人員,咱們更但願的是少修改代碼,儘可能從配置着手也就是設計模式的根本原則之一:開放封閉原則。若是我要增長新的產品,那麼修改就比較大了,對於業務來說仍是能夠接受的。可是若是能夠作到不修改代碼是最好的。上一份工做中,我印象最深的一句話就是我上司對我說的"能不改代碼就別改,能寫進配置裏的就寫到配置裏"。所以咱們將要增長的工廠類寫到配置裏面。如此,新的產品類型和工廠類型即使在系統上線後仍能夠經過修改配置文件的方式不斷補充。可是,還有一個問題,咱們仍然須要爲每"類"抽象產品定製特定的工廠接口並實現之,也就是"多頭管理"問題。泛型能夠用來解決這個問題,咱們定義一個泛型工廠便可。代碼以下:設計模式
/// <summary> /// 工廠接口定義 /// </summary> /// <remarks> /// TTarget: 抽象產品類型 /// TSource: 具體產品類型 /// </remarks> public interface IFactory { #region config and register type mapping /// <summary> /// 若是須要同時加載配置文件中定義的映射關係,能夠按照SRP的原則定義獨立的配置類型。 /// 由該配置類型調用這兩個接口爲Factory加載配置信息 /// </summary> IFactory RegisterType<TTarget, TSource>(); // 注入產品 IFactory RegisterType<TTarget, TSource>(string name); // 注入產品 #endregion #region factory method TTarget Create<TTarget>(); TTarget Create<TTarget>(string name); #endregion } public sealed class TypeRegistry { /// <summary> /// default name in type mappings /// </summary> readonly string DefaultName = Guid.NewGuid().ToString(); /// <summary> /// Type : TTarget, 抽象產品類型 /// IDictionary<string ,Type> /// string : name /// Type : TSource, 具體產品類型 /// </summary> IDictionary<Type, IDictionary<string, Type>> registry = new Dictionary<Type, IDictionary<string, Type>>(); public void RegisterType(Type targetType, Type sourceType) { RegisterType(targetType, sourceType, DefaultName); } public void RegisterType(Type targetType, Type sourceType, string name) { if(targetType == null) throw new ArgumentNullException("targetType"); if(sourceType == null) throw new ArgumentNullException("sourceType"); if(string.IsNullOrEmpty(name)) throw new ArgumentNullException("name"); if (!registry.TryGetValue(targetType, out IDictionary<string, Type> subDictionary)) { subDictionary = new Dictionary<string, Type> { { name, sourceType } }; registry.Add(targetType, subDictionary); } else { if (subDictionary.ContainsKey(name)) throw new Exception($"{name}重複"); subDictionary.Add(name, sourceType); } } public Type this[Type targetType, string name] { get { if (targetType == null) throw new ArgumentNullException("targetType"); if (string.IsNullOrEmpty(name)) throw new ArgumentNullException("name"); if (registry.Count() == 0) return null; return (registry.Where(x => x.Key == targetType)).FirstOrDefault().Value .Where(x => string.Equals(name, x.Key)).FirstOrDefault().Value; } } public Type this[Type targetType] { get { return this[targetType, DefaultName]; } } } public class Factory : IFactory { protected TypeRegistry registry = new TypeRegistry(); #region IFactory Members public IFactory RegisterType<TTarget, TSource>() { registry.RegisterType(typeof(TTarget), typeof(TSource)); return this; } public IFactory RegisterType<TTarget, TSource>(string name) { registry.RegisterType(typeof(TTarget), typeof(TSource), name); return this; } public TTarget Create<TTarget>() { return (TTarget)Activator.CreateInstance(registry[typeof(TTarget)]); } public TTarget Create<TTarget>(string name) { return (TTarget)Activator.CreateInstance(registry[typeof(TTarget), name]); } #endregion }
上面的示例代表新的工廠類型不只能夠完成經典工廠方法模式所但願實現的各項要求,也知足抽象工廠的要求,同時他能夠做爲整個項目一個獨立的並且是惟一的工廠入口,供項目中各子系統訪問和使用。緣由在於它的底層將工廠接口與抽象產品類型的依賴關係變成基於CLR"萬能工廠"類型Activator基於參數Type的構造。
工廠管理的是其內的產品。咱們的工廠接口IFactory有兩個功能,一個是往工廠中注入產品,一個是建立指定產品的實例。藉助RegisterType將配置文件中定義的類型映射方希加載到新的具體工廠類型中,也就是重載函數中的參數(name)。咱們經過字典Dictionary來管理維護工廠內的產品,將抽象產品也就是接口或是抽象類做爲key,要考慮到同一接口能夠有多個不一樣的實現,所以咱們再維護一個實現類的字典,使用一個惟一的標識做爲key就行,value就是實現類。app