【问题标题】:Factory Pattern, selecting by Property工厂模式,按属性选择
【发布时间】:2016-07-05 11:48:40
【问题描述】:

我有一个(不断增长的)数据生成器列表。我需要的生成器是由工厂类创建的。生成器都实现了一个通用接口,其中包括一个静态字符串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,所以它是可用的。 从接口切换到抽象类。

【问题讨论】:

  • 我发现这种方法的一个问题是,为了获得生成器的“名称”属性,您需要一个对象实例
  • 这就是我将属性设为静态的原因。如果我没记错的话,这应该不需要实例。
  • 接口不能有静态成员..
  • @stuartd 好评。我可以使用抽象类而不是接口。 - 编辑:完成。
  • @MilConDoin 在您的abstract 类上使用类似static 的字段/属性会导致每次设置它时都会被覆盖。您可能需要重新考虑使用static 字段

标签: c# design-patterns factory


【解决方案1】:

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

拥有一个工厂来通过某个键获取您需要的逻辑类的实例 - 我相信这是一个好方法。这是我自己经常使用的一种模式。关于您拥有密钥的方式 - 我宁愿不将其作为 static 成员(不管接口不能有静态成员的事实),而是作为 property 并添加一个基类到IDataGenerator。该基类将有一个构造函数来获取name - 这样您创建的每个新DataGenerator 都必须设置它并且您不会忘记。


关于将name 用作string - 我个人更喜欢“强类型”。我的意思是,如果我用字符串传递Gen . 2 而不是Gen.2,我只会在运行时发现这个问题。可能的其他方式(如果你愿意,因为一个简单的字符串也可以 - 个人喜好):

  • enum 替换字符串
  • 为您的所有值创建一个带有静态只读字符串的静态类 - 然后在您的代码中使用这些值。您可以获得智能感知的好处,并且不会弄错字符串但比枚举更好 - 您仍然可以传递不在“列表”中的字符串,因此您可以将新字符串添加为附加组件。
  • 有一个RequestGenerator 对象,每个GeneratorIDataGenerator<TGeneratorRequest>。这可能有点矫枉过正,但如果您还有创建 DataGenerator 所需的额外信息,它们之间存在差异,请考虑一下。

如何找到所有生成器?对接口的每个实现/命名空间的每个成员的反射(对于生成器 + 它们的接口是唯一的)?

是的,反思可能是一个很好的方法。但是,我建议阅读Dependency InjectionIoC Containers,例如Castle Windsor。有些东西已经为你实现了,那么为什么要重新发明轮子:)

DI 在我看来是一个改变生活的概念

将这种工作方式称为工厂是否正确,或者这是某种不同的模式?

嗯。这是一个Factory

工厂中的魔法 GetImplementations() 应该通过反射完成还是以其他方式完成?

查看问题 2 的答案

【讨论】:

  • 您的带有包含静态常量字符串的静态类的附录想法已经实现 :) 为简单起见,我在上面的示例中使用了魔术字符串。
  • 好:)。我还要说,为工厂选择“密钥”的方式因代码的公开方式而异。如果它隐藏在外观后面并且是您自己的实现细节,那么哪个选项并不重要。但是,如果它以某种方式暴露给外部使用,我更喜欢 enumTRequest 选项 - 对于其他人来说,它比 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 的实例时,它会自动包含它们。

【讨论】:

  • 我正在使用 NInject。仅仅在构造函数中有一个接口数组是不够的,所以我之前可能必须做一些绑定。现在要确保有一些简单的方法可以做到这一点,而不必进行扩展反射,我可以在没有 NInject 的情况下直接完成。
  • 我已经使用 Ninject 多年了,数组可以正常工作。只需注册您拥有的所有实例,它就会被注入。如果我没记错的话,以下是支持的 []、IEnumerable、List。请注意,当通过 IEnumerable 注入时,它会延迟加载并在每个枚举上重新评估,所以要小心那个。
  • 是否有一种简单而自动的方法来注册所有实现接口/从父类派生的实例?我不太擅长 NInject。
猜你喜欢
  • 2016-07-05
  • 1970-01-01
  • 1970-01-01
  • 2012-02-15
  • 1970-01-01
  • 2016-04-06
  • 1970-01-01
  • 2012-08-21
  • 1970-01-01
相关资源
最近更新 更多