簡單的工廠類的一個使用場景是,假設有一個基類 BaseClass,和一系列的子類 A,B,C,工廠類根據某個參數,例如字符串 “A”, “B”, “C” 創建出相應的子類。 舉例如下:
public class Factory
{
public static BaseClass Create(string name)
{
switch (name)
{
case "A": return new A();
case "B": return new B();
case "C": return new C();
default: throw new ArgumentException("Wrong Name");
}
}
}
這裡的一個問題是,當子類增加或減少時,Factory 類 需要相應的改動。 有沒有辦法可以只是改動子類本身,而不用修改Factory類呢,當然有,這裡我舉一個簡單的實現。
基本思想是在每個子類上附加一個 Attribute,定義如下:
[AttributeUsage(AttributeTargets.Class)]
public class FactoryKeyAttribute : Attribute
{
public object Key { get; set; }
}
假設我們有基類和子類實現如下
public abstract class BaseClass {}
[FactoryKey(Key = "Standard")]
public class Standard : BaseClass {}
[FactoryKey(Key = "Enterprise")]
public class Enterprise : BaseClass {}
[FactoryKey(Key = "Lite")]
public class Lite : BaseClass {}
假設這些類都在同一個 Assembly中 (對於不在同一個Assembly的,實現會稍微復雜些)工廠類需要預先加載 Key => Type 的Mapping,然後根據Key創建不同的實例,實現如下:
public class Factory<TKey, TBaseClass>
{
private static readonly IDictionary<TKey, Type> TypeDict = Init();
private static IDictionary<TKey, Type> Init()
{
var dict = from type in Assembly.GetExecutingAssembly().GetTypes()
let key = (FactoryKeyAttribute)Attribute.GetCustomAttribute(type, typeof(FactoryKeyAttribute))
where key != null && typeof(TBaseClass).IsAssignableFrom(type)
select new { Key = key, Value = type };
return dict.ToDictionary(kvp => (TKey)kvp.Key.Key, kvp => kvp.Value);
}
public static TBaseClass CreateInstance(TKey key)
{
Type type;
if (TypeDict.TryGetValue(key, out type))
{
return (TBaseClass)Activator.CreateInstance(type);
}
throw new ArgumentException("Incorrect Key!");
}
}
使用方法也很簡單:
BaseClass s = Factory<string, BaseClass>.CreateInstance("Standard");
BaseClass l = Factory<string, BaseClass>.CreateInstance("Lite");
BaseClass e = Factory<string, BaseClass>.CreateInstance("Enterprise");
對於其他類型的Key,比如 Enum,或其他類型的基類,改變Factory 的類型參數即可。