2016-07-05 71 views
7

我有一个(增长的)数据生成器列表。我需要的生成器是由工厂类创建的。生成器都实现了一个通用接口,其中包括一个静态字符串name工厂模式,按属性选择

我想要做的是:用上述名字的字符串参数调用factory.Create方法。 create方法找到具有该名称的生成器并返回该生成器的新实例。

在我看来这样做的奖励:我只需要添加新的生成器类,而无需编辑工厂。

问:

  1. 这是处理这个问题的好办法?
  2. 我怎样才能找到所有的发电机?反映每个接口/名称空间的每个成员(对于生成器+它们的接口都是唯一的)的实现?
  3. 称这种工厂工作方式是正确的,还是这种不同的模式?

最后,我会打电话的工厂像这样(简化):

//Caller 
public DataModel GetData2() 
{ 
    var generator = new DataFactory().Create("Gen.2"); 
    return generator.GetData(); 
} 

//Factory 
public class DataFactory 
{ 
    public AbstractDataGenerator Create(string type) 
    { 
     //Here the magic happens to find all implementations of IDataGenerator 
     var allGenerators = GetImplementations(); 
     var generator = allGenerators.FirstOrDefault(f => f.name == type); 
     if (generator != null) 
      return (AbstractDataGenerator)Activator.CreateInstance(generator); 
     else 
      return null; 
    } 
} 

//Interface 
public abstract class AbstractDataGenerator 
{ 
    public static string name; 
    public abstract DataModel GetData(); 
} 

//Data-Generators 
public class DataGen1 : AbstractDataGenerator 
{ 
    public static string name = "Gen.1"; 
    public DataModel GetData() 
    { 
     return new DataModel("1"); 
    } 
} 
public class DataGen2 : AbstractDataGenerator 
{ 
    public static string name = "Gen.2"; 
    public DataModel GetData() 
    { 
     return new DataModel("2"); 
    } 
} 

应在工厂魔术GetImplementations()通过反射来完成,或者以某种方式有什么不同?我应该使用完全不同的方法吗?

由于答案涉及IoC和DI:该项目已经使用NInject,因此它将可用。 从接口切换到抽象类。

+0

一个问题我已经使用这种方法发现的是,为了获得发电机的“名称”属性,你_need的object_的一个实例。 – stuartd

+0

这就是为什么我使属性静态。如果我没有弄错,这应该删除一个实例的需要。 – MilConDoin

+0

接口不能有静态成员.. – stuartd

回答

4

这是处理这个问题的好方法吗?

拥有一个工厂来获得您需要的逻辑类的一个实例 - 我相信这是一个好方法。这是我自己使用很多的模式。关于你有没有密钥的方式 - 我宁愿不要将它作为一个static成员(不管接口不能有静态成员),而只是作为property,并向IDataGenerator添加基类。该基类将有一个构造函数,将得到name - 这样你创建的每个新的DataGenerator将不得不设置它,你不会忘记。


关于具有namestring - 我个人比较喜欢拥有它“强类型”。我的意思是,如果我通过Gen . 2而不是Gen.2与字符串我只会在运行时发现此问题。其它可能的方式(如果你想,因为一个简单的字符串也没关系 - 品味问题):

  • enum
  • 替换字符串与您的所有值静态只读字符串静态类 - 那么在你的代码中使用这些值。你得到了intellisense的好处,并且没有弄错字符串,但比enum更好 - 你仍然可以传递不在“列表”中的字符串,因此你可以添加新的字符串作为附加组件。
  • 有一个RequestGenerator的对象,每个GeneratorIDataGenerator<TGeneratorRequest>。这可能是一种矫枉过正,但如果你还需要额外的信息来创建一个不同的DataGenerator然后考虑它。

我怎样才能找到所有的发电机?反映每个接口/名称空间的每个成员(对于生成器+它们的接口都是唯一的)的实现?

是的,反射可以是一个很好的方法。不过,我建议读入Dependency InjectionIoC Containers,例如Castle Windsor。有些事情在那里,已经实现了它的你,为什么要重新发明轮子:)

DI在我看来是一个生命换概念

是否正确调用这种工作方式一个工厂,还是这种不同的模式?

Yap。这是一个Factory

应该通过反射或以某种不同的方式在工厂中的魔术GetImplementations()?对于问题

查看答案2

+0

您的附加构思包含静态常量字符串的静态类已经实现:)我在上面的例子中使用了魔术字符串,而不是简单的。 – MilConDoin

+0

好:)。我也会说,为工厂选择“关键”的方式因代码的暴露方式而有所不同。如果它隐藏在门面之后,并且是您自己的实现细节,那么它对于哪个选项无关紧要。然而,如果它暴露给外部使用,我更喜欢'enum'或'TRequest'选项 - 对于其他人来说,比'string'更清晰的界面 –

2

这是构造函数注入可以真正发光的地方。研究依赖注入工具并雇用一个!它也检查你的“奖金”请求。

这是你的工厂可能会是什么样构造器注入:

public class DataFactory 
{ 
    private Dictionary<string, IDataGenerator> generators; 

    public DataFactory(IDataGenerator[] generatorReferences) 
    { 
     this.generators = generatorReferences 
      .ToDictionary(k => k.name, v => v); 
    } 
    public IDataGenerator Create(string type) 
    { 
     IDataGenerator generator = null; 
     this.generators.TryGetValue(type, out generator); 
     return generator; 
    } 
} 

大多数DI软件具有自动扫描组件某种类型(如IDataGenerator)的实现,并与自己注册的,当它的能力构造一个DataFactory的实例,它会自动包含它们。

+0

我正在使用NInject。仅仅在构造函数中有一个接口数组是不够的,所以我可能必须先做一些绑定。现在要确定,有一些简单的方法可以做到这一点,而无需进行扩展反射,这可以在没有NInject的情况下直接完成。 – MilConDoin

+0

我一直在使用Ninject多年,一个数组将会正常工作。只需注册您拥有的所有实例并注入即可。如果我正确地记得以下是支持的[],IEnumerable ,列表。请注意,当通过IEnumerable 注入时,它会在每个枚举中进行延迟加载和重新评估,因此请注意这一点。 –

+0

是否有一种简单且自动的方式来注册实现从父类接口/派生的所有实例?我对NInject不太满意。 – MilConDoin