Dependency injection 用于在复杂层次结构中注入运行时依赖项的抽象工厂

Dependency injection 用于在复杂层次结构中注入运行时依赖项的抽象工厂,dependency-injection,abstract-factory,Dependency Injection,Abstract Factory,给定以下类层次结构 A级需要B级 B类需要C类 我们得到的依赖关系图如下所示: ClassA --> ClassB --> ClassC 所以,如果我们使用DI,我们将ClassC注入ClassB,将ClassB注入ClassA 但是现在让我们假设ClassC是一个运行时依赖项(例如某种策略)。注入运行时依赖项的建议方法是引入一个抽象工厂,如 ClassCFactory 现在我们可以将ClassCFactory注入到ClassB中,并得到下面的图 ClassA -->

给定以下类层次结构

  • 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容器将是无用的

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

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

当您必须从一系列相关的对象族中生成对象,但在运行时之前不知道是哪个族时,可以选择使用抽象工厂。您的示例中没有显示这种情况,因此YAGNI/KISS表示不使用

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

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


国际奥委会也被称为好莱坞原则——“不要打电话给我们,我们会打电话给你”。“我们”是谁,以及“呼叫您”部分将在何时发生,这取决于您的应用程序,没有硬性规定。如果您担心ObjectA可能对ClassC了解太多,只需将其注入委托给其他人即可。DI容器可以在这方面起到很大的作用。

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

然而,在你上面的例子中,我认为你的两个选择之间没有任何显著的区别

在一个案例中,你有

ObjA.ObjB.SelectC(MyRuntimeValue) 
另一方面,你也会

ObjA.ObjB.ObjC
在我看来,这两种选择都有相同的优点和缺点


为了避免违反德米特定律,您必须添加许多传递函数,正如您所注意到的。这并不总是值得权衡的,但肯定是要考虑的。

OK,我会给你更具体的含义类:ClassC是一个抽象类加密,对于不同类型的加密有不同的实现。ClassB是一个装饰器,它用加密来装饰文件流。ClassA是某种可序列化的对象,它需要序列化一个流。什么决定了要使用哪种类型的加密?您可以让ClassA的消费者决定并将3个俄罗斯玩偶装配在一起(加密、FileTreeamDecorator和ClassA),或者一些外部对象可以选择一个加密,将其放入FileStreamDecorator并将其全部传递给ClassA的消费者。。。选择正确的加密取决于我们是加载还是保存。加载时,我们询问使用哪种加密的报头,然后询问用户其密码短语并开始读取和解密。保存时,我们必须使用与加载时相同的加密。导出时有一个特殊情况,用户最终可以决定使用哪种加密。关于可能创建streamdecorator的外部对象:如何使用DI容器实现这一点。同样的问题。我们需要一个带有加密和流的流装饰器。两者都是运行时依赖项。我可以简单地创建一个新的StreamDecorator并通过方法注入来注入它。但是decorator不是DI容器的一部分。我看不到将
MD5EncryptionFactory
传递给decorator有什么好处,只要将
MD5Encryption
传递给decorator即可(这两个都是我所说的“手动注入”,因为外部执行上下文感知对象必须决定注入哪种类型)