如何将依赖注入与Factory模式结合使用


10

考虑一个负责解析任何给定类型文件的模块。我已经在这里进行了解释,我正在考虑使用策略模式来解决此问题。在继续此问题之前,请参阅链接的帖子。

考虑需要product.xml文件内容的类B。此类将需要实例化Parser接口的适当具体实现程序以解析XML文件。我可以将适当的具体实现者的实例委派给Factory,以使B类“拥有” Factory。但是,类B随后将“依赖”于工厂以实例化具体的实现者。这意味着将需要将类B中的构造函数或setter方法传递给Factory。

因此,需要解析文件的Factory和B类将紧密地结合在一起。我了解到目前为止,我所解释的内容可能完全错误。我想知道是否可以在要注入的依赖项是Factory的情况下使用依赖项注入,以及实现此目标的正确方法是什么,以便在单元测试中利用诸如模拟Factory的优势。

Answers:


8

正确的方法是依赖一个接口,然后将该接口的实现注入到B类中。

接口只是您可以依赖的最薄的东西-我将其比作尝试捕获一缕烟雾。代码必须耦合到某些东西,否则它什么也不会做,但是耦合到接口的过程几乎可以做到分离,但是接口可以提供所需的所有功能。

因此,让B类的构造函数为所需的类提供一个接口,并让工厂将该类作为该接口的实现者进行生产。不要依赖工厂,不必依赖接口,并让工厂提供该工厂的实现。

所以是的,您将使用依赖项注入,但是这没有错。依赖注入-特别是简单的构造函数注入-应该是做事的“正常”方式。只需将新事物推迟到您的应用中(并尽可能靠近的第一行main),然后将新调用隐藏在专门用于创建事物的类中即可。

底线:不要犹豫,注入依赖项。那应该是正常的做事方式。


构造函数注入是否会尽可能长时间推迟更新?
JeffO

这是不正确的-我已将其固定为“尽可能早地进入您的应用”。
Nick Hodges

是。我当时在考虑依赖解析器接口而不是工厂。现在,如果另一个类使用类B,则它必须依赖于类B所依赖的接口。这将一直持续到顶级课程。这个顶级类最终将不得不使用工厂实例化解析器的适当具体实例。顶级类应该依赖工厂还是应该在其方法内部直接使用工厂?
2013年

1
得好

9

我认为您的前提在这里有点混乱,您说的是注入工厂,但是工厂模式是一种创建模式,其目的是做一个依赖项注入框架的子集,而当DI框架并不普遍时,这种模式就是因此很有用。但是,如果您具有DI框架,那么您不再真正需要工厂,因为DI框架可以实现工厂将要实现的目的。

就是说,让我解释一下有关依赖注入以及您通常如何使用它。

进行依赖注入的方法有很多种,但是最常见的是构造函数注入,属性注入和直接DIContainer。我将谈到构造函数注入,因为在大多数情况下,属性注入是错误的方法(有时是正确的方法),DIContainer访问不是可取的,除非您绝对不能使用其他任何一种方法。

构造函数注入是您具有依赖项接口的地方,以及知道该依赖项具体实现的DIContainer(或工厂),以及需要依赖该接口的对象的任何地方,在构造时,您将实现从工厂移交给它。

IDbConnectionProvider connProvider = DIContainer.Get<IDbConnectionProvider>();
IUserRepository userRepo = new UserRepository(connProvider);
User currentUser = userRepo.GetCurrentUser();

许多DI框架可以大大简化此操作,您的DIContainer会在其中检查UserRepository的构造函数以了解其具体实现的接口,并自动将这些接口交给您。这种技术通常称为控制反转,尽管DI和IoC都是互换性很强(如果有的话)的术语。

现在,如果您想知道总体代码如何访问DIContainer,那么您可以使用一个静态类来访问它,或者更合适的是,大多数DI框架都允许您创建一个DIContainer,实际上它的行为就像内部单例字典的包装器,它知道对于给定接口而言具体的类型。

这意味着,您可以在代码中的任何位置更新DIContainer,并有效地获得与您已配置的DIContainer相同的DIContainer,以了解您的界面与混凝土的关系。将DIContainer隐藏在不应直接与其交互的代码部分中的通常方法是,仅确保仅必要的项目具有对DI框架的引用。


我不同意第一段。在某些情况下,您依赖于由DI容器注入的工厂(实现接口)。
2013年

我认为您错了这一点,因为OP并未提及DI框架或DI容器。
布朗

@Jimmy Hoffa虽然您提出了一些非常有趣的观点,但我知道IoC容器,例如Spring和依赖注入的cconcept。我担心的是在这种情况下不使用IoC框架的情况,您需要编写一个类来为您实例化依赖项。如果A类依赖于接口B,而C类使用了A类,则C类依赖于接口B。有人需要给C类提供B类证书。C类是否应该取决于cla
CKing

如果类A依赖于接口B,而类C使用了类A,则类C依赖于接口B。有人需要给类C一个接口B。类C然后应该依赖于接口B还是应该依赖于实例化接口B的类。依赖即工厂。您似乎已经在回答中回答了这个问题,但信息过多:)
CKing 2013年

@bot就像我在序言中所说的那样,在很大程度上,您可以将工厂视为DI容器功能的子集,这并非像Doc Brown所提到的在所有情况下都是正确的,但请看一下我的代码示例并替换DIContainerDbConnectionFactory和概念仍然立场是,您将从DI / Factory / etc中检索具体实现,并在构建时将其交付给该类型的消费者。
Jimmy Hoffa

5

您可以像通过其他方式一样通过依赖项注入来传递工厂,不要让情况的递归性使您感到困惑。我不知道要实现它还有什么要说的-您已经知道如何进行依赖注入。

我使用DI定期注入工厂。


1
如果类依赖于Factory,则在对该类进行单元测试时需要模拟工厂。您如何去做?让类依赖于工厂接口?
2013年

4

注入工厂没有错。这是一种标准方法,如果您无法在“父”对象的构建过程中决定需要哪种依赖关系。我认为例子将最好地解释它。我将使用c#,因为我不懂Java。


class Parent
{
    private IParserFactory parserFactory;
    public Parent(IParserFactory factory)
    {
        parserFactory = factory
    }

    public ParsedObject ParseFrom(string filename, FileType fileType)
    {
        var parser = parserFactory.CreateFor(fileType);
        return parser.Parse(filename);
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.