1

给定以下类层次结构

  • A类需要B类
  • B类需要C类

我们得到一个像这样的依赖图:

ClassA --> ClassB --> ClassC

因此,如果我们使用 DI,我们将 ClassC 注入到 ClassB 中,将 ClassB 注入到 ClassA 中。

但是现在假设 ClassC 是一个运行时依赖项(例如某种策略)。注入运行时依赖项的建议方法是引入一个抽象工厂,如

ClassCFactory

现在我们可以将 ClassCFactory 注入 ClassB 并得到下图

ClassA --> ClassB --> ClassCFactory

现在我们在 ClassB 中有一个方法,我们可以调用它来让工厂完成它的工作。例如

ObjB.SelectC(MyRuntimeValue)

但现在在我们的应用程序中,我们对 ClassB 一无所知(可能涉及更多层)。一种解决方案可能是在 ClassA 中有一个 SelectC

ObjA.SelectC(MyRuntimeValue) -(calls)-> ObjB.SelectC(MyRuntimeValue)

或者我们只是违反得墨忒耳法则并做类似的事情

ObjA.ObjB.SelectC(MyRuntimeValue) 

我想每个人都同意第二种解决方案不是要走的路。但是第一个解决方案也有几个缺点,特别是如果我们之间有更多的层。

我们也可以将工厂上拉一级来创建 ClassB,但 ClassB 真的是运行时依赖吗?你建议什么解决方案?或者它甚至是一个糟糕的类设计?

恕我直言,最好依赖于对象实际需要什么来完成其工作,而不是依赖于创建所需对象的工厂。但是考虑到这个想法,DI容器将毫无用处......

4

2 回答 2

2

注入运行时依赖项的建议方法是引入抽象工厂

您不一定需要一个抽象工厂来在运行时注入东西。您可以使用 setter 注入或方法注入将简单的依赖项直接传递给您的对象。

当您必须从一系列相关对象中生成对象但直到运行时才知道哪个族时,可以选择抽象工厂。您的示例中没有任何内容表明情况如此,因此 YAGNI/KISS 会指示不要使用。

但现在在我们的应用程序中,我们对 ClassB 一无所知(可能涉及更多层)。

您能否详细说明为什么这在您的场景中是正确的?对我来说,似乎总会有某种执行上下文知道 ClassB 并能够将 C 注入其中。不过,它不必与将 ClassB 注入 A 的执行上下文相同。

IoC 也被称为好莱坞原则 - "Don't call us, we'll call you". 根据您的应用程序,谁"we"是谁以及该"call you"部分将在何时发生可能会有很大差异,没有硬性规定。如果您担心 ObjectA 可能对 ClassC 了解太多,只需将其注入委托给其他人即可。DI 容器可以在这方面提供很大帮助。

于 2013-03-26T10:18:20.943 回答
0

我发现运行时依赖也很麻烦,而且我认为抽象工厂的框架开销是烦人的额外代码,我不想存在。但不幸的是,我还没有想到更好的方法。

但是,在您上面的示例中,您的两种选择之间似乎没有任何显着差异。

在一种情况下,您有

ObjA.ObjB.SelectC(MyRuntimeValue) 

而在另一个你会有

ObjA.ObjB.ObjC

在我看来,这两种选择都有相同的优点和缺点。

正如您所指出的,为了避免违反得墨忒耳定律,您必须添加许多通过功能。这并不总是值得权衡,但绝对值得考虑。

于 2015-09-02T10:21:35.020 回答