给定以下类层次结构
我们得到一个像这样的依赖图:
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容器将毫无用处......
答案 0 :(得分:2)
注入运行时依赖性的建议方法是引入一个 抽象工厂
您不一定需要抽象工厂来在运行时注入内容。您可以使用setter注入或方法注入将简单的依赖项直接传递给对象。
当您必须从一系列相关对象系列生成对象但在运行时之前不知道哪个系列时,抽象工厂可以是一个选项。您的示例中没有任何内容表明情况如此,因此YAGNI / KISS会表示不使用。
但是现在在我们的应用程序中,我们对ClassB一无所知 (也许还有更多层次。)
您是否可以提供有关在您的方案中为何如此的详细信息?对我来说,似乎总会有某种执行上下文知道ClassB并且能够将C注入其中。但它不必与将ClassB注入A的执行上下文相同。
IoC也被称为好莱坞原则 - "Don't call us, we'll call you"
。 "we"
是谁以及"call you"
部分发生的时间根据您的申请而有很大差异,没有严格的规则。如果您担心ObjectA可能对ClassC了解太多,只需将其注入委托给其他人。 DI容器可以帮助很多。
答案 1 :(得分:0)
我发现运行时依赖性也很麻烦,我认为抽象工厂的框架开销是令人烦恼的额外代码,我不想存在。但不幸的是,我还没有想到更好的方法。
然而,在上面的例子中,我的两个选择之间似乎没有任何显着差异。
在一个案例中,你有
ObjA.ObjB.SelectC(MyRuntimeValue)
在另一个你会有
ObjA.ObjB.ObjC
这两个选项在我看来都有相同的优点和缺点。
为了避免违反Demeter法则,你必须添加大量的传递函数,如你所知。这并不总是值得折衷,但绝对值得考虑。