工厂模式,按财产

时间:2016-07-05 11:48:41

标签: c# design-patterns factory

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

我想做什么:使用上述名称的字符串参数调用factory.Create方法。 create方法查找具有此名称的生成器,并返回所述生成器的新实例。

我认为这样做的好处是:我只需添加新的生成器类而无需编辑工厂。

问题:

  1. 这是处理这个问题的好方法吗?
  2. 我如何找到所有发电机?反映接口的每个实现/命名空间的每个成员(对于生成器及其接口是唯一的)?
  3. 这种工厂工作方式是正确的,还是这种模式不同?
  4. 最后我会像这样称呼工厂(简化):

    //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,因此可以使用。 从接口切换到抽象类。

2 个答案:

答案 0 :(得分:4)

  

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

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

关于将name作为string - 我个人更喜欢让它“强烈打字”。我的意思是,如果我用Gen . 2而不是Gen.2传递字符串,我将仅在运行时发现此问题。可能的其他方式(如果你想,因为一个简单的字符串也很好 - 一个品味问题):

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

如何找到所有发电机?反映接口的每个实现/命名空间的每个成员(对于生成器及其接口是唯一的)?

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

在我看来,

DI是改变生活的概念

  

这种工厂工作方式是正确的,还是这种模式不同?

邑。这是一个Factory

  

工厂中的神奇GetImplementations()是应该通过Reflection完成还是以某种方式不同?

见问题2的答案

答案 1 :(得分: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实例时,它会自动包含它们。