相比較傳統(tǒng)的工廠模式IFactory/Concrete Factory會(huì)反復(fù)引用并編譯代碼
但是作為開(kāi)發(fā)人員,我們更希望的是少修改代碼,盡量從配置著手也就是設(shè)計(jì)模式的根本原則之一:開(kāi)放封閉原則。如果我要增加新的產(chǎn)品,那么修改就比較大了,對(duì)于業(yè)務(wù)來(lái)講還是可以接受的。但是如果可以做到不修改代碼是最好的。上一份工作中,我印象最深的一句話就是我上司對(duì)我說(shuō)的"能不改代碼就別改,能寫進(jìn)配置里的就寫到配置里"。因此我們將要增加的工廠類寫到配置里面。如此,新的產(chǎn)品類型和工廠類型即便在系統(tǒng)上線后仍可以通過(guò)修改配置文件的方式不斷補(bǔ)充。但是,還有一個(gè)問(wèn)題,我們?nèi)匀恍枰獮槊?quot;類"抽象產(chǎn)品定制特定的工廠接口并實(shí)現(xiàn)之,也就是"多頭管理"問(wèn)題。泛型可以用來(lái)解決這個(gè)問(wèn)題,我們定義一個(gè)泛型工廠即可。代碼如下:
/// <summary>
/// 工廠接口定義
/// </summary>
/// <remarks>
/// TTarget: 抽象產(chǎn)品類型
/// TSource: 具體產(chǎn)品類型
/// </remarks>
public interface IFactory
{
#region config and register type mapping
/// <summary>
/// 如果需要同時(shí)加載配置文件中定義的映射關(guān)系,可以按照SRP的原則定義獨(dú)立的配置類型。
/// 由該配置類型調(diào)用這兩個(gè)接口為Factory加載配置信息
/// </summary>
IFactory RegisterType<TTarget, TSource>(); // 注入產(chǎn)品
IFactory RegisterType<TTarget, TSource>(string name); // 注入產(chǎn)品
#endregion
#region factory method
TTarget Create<TTarget>();
TTarget Create<TTarget>(string name);
#endregion
}
/// <summary>
/// 充當(dāng) 依賴注入 的角色
/// </summary>
public sealed class TypeRegistry
{
/// <summary>
/// default name in type mappings
/// </summary>
readonly string DefaultName = Guid.NewGuid().ToString();
/// <summary>
/// Type : TTarget, 抽象產(chǎn)品類型
/// IDictionary<string ,Type>
/// string : name
/// Type : TSource, 具體產(chǎn)品類型
/// </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}重復(fù)");
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
}
上面的示例表明新的工廠類型不僅可以完成經(jīng)典工廠方法模式所希望實(shí)現(xiàn)的各項(xiàng)要求,也滿足抽象工廠的要求,同時(shí)他可以作為整個(gè)項(xiàng)目一個(gè)獨(dú)立的而且是唯一的工廠入口,供項(xiàng)目中各子系統(tǒng)訪問(wèn)和使用。原因在于它的底層將工廠接口與抽象產(chǎn)品類型的依賴關(guān)系變成基于CLR"萬(wàn)能工廠"類型Activator基于參數(shù)Type的構(gòu)造。
工廠管理的是其內(nèi)的產(chǎn)品。我們的工廠接口IFactory有兩個(gè)功能,一個(gè)是往工廠中注入產(chǎn)品,一個(gè)是創(chuàng)建指定產(chǎn)品的實(shí)例。借助RegisterType將配置文件中定義的類型映射方希加載到新的具體工廠類型中,也就是重載函數(shù)中的參數(shù)(name)。我們通過(guò)字典Dictionary來(lái)管理維護(hù)工廠內(nèi)的產(chǎn)品,將抽象產(chǎn)品也就是接口或是抽象類作為key,要考慮到同一接口可以有多個(gè)不同的實(shí)現(xiàn),因此我們?cè)倬S護(hù)一個(gè)實(shí)現(xiàn)類的字典,使用一個(gè)唯一的標(biāo)識(shí)作為key就行,value就是實(shí)現(xiàn)類。
|