一个简单的 Generic Factory 类


简单的工厂类的一个使用场景是, 假设有一个基类 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 的类型参数即可

简单的工厂类的一个使用场景是, 假设有一个基类 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 的类型参数即可


« 
» 
快速导航

Copyright © 2016 phpStudy | 豫ICP备2021030365号-3