【问题标题】:How to initialize a factory?如何初始化工厂?
【发布时间】:2018-10-29 06:57:23
【问题描述】:

我有一个制造汽车的工厂...它是一个基本工厂,它接受汽车的名称,查找所有实现 ICar 的类,根据汽车名称选择正确的类型并使用反射初始化汽车。

public class CarFactory
{
    Dictionary<string, Type> cars;

    public ICar CreateInstance(string carName)
    {
        // choose type of class from all classes that implement ICar 
        Type t = GetTypeToCreate(carName); 
        return Activator.CreateInstance(t) as ICar;
    }

    // some more code...
}

现在,我有一个使用CarFactory 的服务。在我的服务中初始化 CarFactory 的正确方法是什么?

一种解决方案是注入工厂,但工厂模式本身就是 IoC 的一种形式,注入工厂感觉很奇怪。根据Nikola Malovic

将工厂与 IoC 一起使用没有多大意义,因为 IoC 容器在某种意义上是“通用抽象工厂”。

换句话说,根据我的经验,任何时候我考虑添加一个 工厂我以更简单的基于 IoC 的解决方案结束,这就是为什么我 敢说“IoC杀了工厂之星”

上述内容对我来说完全有意义,尽管我还没有想出用 DI 替换我的工厂的解决方案。

所以我的问题是(对于那些使用工厂模式的人)我应该如何初始化它?


为了避免让问题变得很长,我在这个问题中使用了一个简化的CarFactory 示例。我已经发布了完整的示例here

【问题讨论】:

  • 引用说工厂和 DI 容器做同样的工作,你不需要两种工具来完成一项工作。所以你的问题的答案是没有正确的方法来初始化工厂。不要使用工厂。使用 DI 容器。
  • @jaco0646:谢谢。如果是这种情况,那么就排除了工厂的注入......可以说使用静态工厂是可行的方法(而不是在构造函数中实例化它)吗?就像一个静态 DI 容器?
  • 你说的'初始化是什么意思?'
  • @MarkSeemann:谢谢,我的意思是我应该使用静态工厂,在Service的构造函数中实例化工厂还是注入工厂?

标签: design-patterns dependency-injection inversion-of-control factory-pattern


【解决方案1】:

像往常一样,对于这类问题,答案是It Depends™。这取决于您尝试解决的问题。虽然这个问题被标记为 dependency-injectioninversion-of-control (在一定程度上the same idea)这些都不是目标,而只是一种手段实现正确的目标。

通常,目标是解耦行为。这就引出了第一个问题,那么:

为什么CreateInstance 返回ICar 实例?

我在这里假设ICar 是一个接口。 ICar 是多态的吗?您是否有多个该接口的实现?

一个名为 car 的对象对我来说听起来像一个实体,而那些 should usually not be injected,因为它们通常代表数据,而不是行为。

不过,为了争论,我们假设ICar 是真正的多态。

如此处所示,CarFactory 看起来是确定性的。但是很难说,因为没有显示整个实现。但是,如果是referentially transparent,那为什么还要初始化呢?

不能只是一个静态函数吗?

public static class CarFactory
{
    public static ICar CreateInstance(string carName)
    {
        // choose type of class from all classes that implement ICar 
        Type t = GetTypeToCreate(carName); 

        return Activator.CreateInstance(t) as ICar;
    }

    // some more code...
}

【讨论】:

  • 非常感谢。是的,ICar 是多态的,而 Car 是我的 ViewModel。我试图避免长篇大论,因此简化了我的代码。我用指向实际代码here 的链接更新了问题
  • 在上面的答案中,您是说:“一个名为 car 的对象在我看来就像一个实体,通常不应该注入这些对象,因为它们通常代表数据,而不是行为。”我已经阅读了您链接的其他答案,但是您在其中解释说实体不应依赖于其他类,例如存储库...请,您能否解释为什么通常不应注入 Car 之类的实体?
  • @HoomanBahreini 像Car 这样的实体通常代表域数据,并且在您的系统中通常有数千甚至数百万个Car 对象可用。如果要注射,你选哪一种?
【解决方案2】:

最好将依赖项(构造函数、属性、方法)注入到您的类型中。并且最好避免使用 new-operator 创建依赖。

为了使它更好,您应该为您定义一个接口CarFactory (ICarFactory) 并将您的服务更改为依赖于该类型。

在测试中,您可以模拟 ICarFactory 并为您的测试用例提供特殊实现。

【讨论】:

  • 非常感谢。是的,我会使用 ICarFactory 进行 DI ......所以注入工厂是一种常见的做法?只是想确保我所做的不是设计气味或类似的东西?
  • @Hooman 我经常将工厂抽象(IFactory)注入我的类型。我认为这不是设计缺陷或气味。
  • 您还可以在 IOC-Container 中注册您的汽车,并在 ICarFactory 后面使用它来为您提供所需的汽车实例。
  • 再次感谢...我用一些建议不要注入工厂的参考更新了我的问题...实际上首先反对使用工厂模式...我开始认为我的问题可能以意见为基础。
  • > 我开始认为我的问题可能是基于意见的 那是真的:-)。我会说使用最适合您的解决方案的方法,但始终使用接口并注入依赖项。
【解决方案3】:

我认为比较依赖注入容器和工厂是错误的。
是的,两者的责任都是创建某种类型的实例。

但是 DI 容器负责实例化一个实例及其所有依赖项基于 DI 配置。通常 DI 容器在应用程序的入口点实例化对象(main 方法或 request 在 Web 应用程序中)。

Factory 将根据应用程序的某些业务逻辑或仅在运行时访问的某些值创建该类型的实例。

Factory 与其他类一样是普通类,可以将其作为依赖项注入。

在您的特定情况下,您仅在运行时知道汽车的名称,因此您将需要一些负责将名称“转换”为汽车实例的类。

【讨论】:

  • 非常感谢法比奥
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-16
  • 2013-11-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多