Dependency injection 依赖注入:将调用图与构造图分离

Dependency injection 依赖注入:将调用图与构造图分离,dependency-injection,call-graph,Dependency Injection,Call Graph,我正在试着锻炼身体,但做起来有点困难 我有一个函数,它定期进入我的数据库,检索产品列表,然后对这些产品运行一系列测试,以确定它们的安全性 如果发现一个或多个产品不安全,我需要通过实例化和分派ProductRecall对象来发出召回 该函数如下所示:(伪代码) 问题是,我是“新的”——在我的调用图中间的产品召回> /代码>对象。这违反了依赖注入的原则。如前所述,如果不创建ProductRecall对象,我就无法测试CheckProductSaftey() 但是,我无法将ProductRecall对

我正在试着锻炼身体,但做起来有点困难

我有一个函数,它定期进入我的数据库,检索产品列表,然后对这些产品运行一系列测试,以确定它们的安全性

如果发现一个或多个产品不安全,我需要通过实例化和分派
ProductRecall
对象来发出召回

该函数如下所示:(伪代码)

问题是,我是“新的”——在我的调用图中间的<代码>产品召回> /代码>对象。这违反了依赖注入的原则。如前所述,如果不创建
ProductRecall
对象,我就无法测试
CheckProductSaftey()

但是,我无法将
ProductRecall
对象传递到我的
SafetyInspector
对象中,因为
SafetyInspector
是确定不安全产品列表的人

我对所有事情都使用构造函数注入,我想继续这样做。另外请注意,我可以随时发出多个
ProductRecall
,因此我不一定只需在构建时将单个
ProductRecall
对象传递给
SafetyInspector


有什么建议吗?谢谢

我认为你实际上需要想出某种
ProductRecallFactory
来代替。注射剂容器支持某种工厂风格的界面是很常见的,或者您可能只是希望让您的SafetyInspector容器知道


编辑:通过“容器感知”,我指的是按照COM的
IObjectWithSite
实现一些接口,以便您的对象可以回调到其父对象。这是一种较弱的依赖注入形式,它部分地撤销了控制反转。如果您是手动执行依赖项注入,请务必注入一个工厂对象。

我认为问题可能在于您的
ProductRecall
实现。具体地说,如果您可以对新创建的对象调用
dispatch()
,这意味着许多实际功能要么隐藏在
ProductRecall
类中,要么
ProductRecall
类具有静态成员和/或单例,以提供它所需的所有其他功能

我建议创建一个名为
ProductRecallDispatcher
的类,该类处理实际调度的复杂性。然后创建其中一个对象,并将其传递给
SafteyInspector
的构造函数。这将使您的
CheckProductSafety
功能如下所示:

void SafteyInspector::CheckProductSaftey()
{
  database.getProducts( list_of_products )

  foreach( list_of_products as p )
  {
    if ( !runBatteryOfTests( p ) )
      unsafe_products.insert( p );
  }

  if ( !unsafe_products.empty() )
  {
    recallDispatcher.recall( unsafe_products );
  }
}

你能澄清一下“容器感知”是什么意思吗?(如果有区别的话,我使用的是手动DI。)您将代码嵌入到标记中,而不是进行代码格式化(每行前面有四个空格)。我为您切换了它,这样我们就可以享受so的漂亮代码着色的好处,但是如果您的选择是有意的,请随时更改回来。啊,哈!我一直在想这是怎么回事。谢谢你的提示!没问题。当我第一次在这里开始时,我手动将所有内容嵌入
标记中。生活与学习!:)嗯,ProductRecall对象内部有相当多的复杂性,这一点您是绝对正确的(正如您所猜测的,它确实依赖于几个单例对象)。也就是说,如果我创建一个ProductRecallDispatcher类,那么在这个新对象中似乎也会遇到同样的问题。具体来说,我必须从ProductRecallDispatcher::recall()内部“新建”ProductRecall对象。这将取决于.recall()函数实际执行的操作。我建议将大部分功能移到dispatcher中,ProductRecall只保存与实际召回相关联的数据(以及帮助创建召回的功能)。“新”-ing问题不大,因为RecCallDispatcher仍然拥有recall对象(可能通过保留指针的STL向量),并且可以在销毁时安全地处理它。顺便说一句,这些单例可能是一个问题,因为使用它们会阻止您进行适当的依赖项注入。重构以删除单例并切换到依赖注入是一项有趣的工作。祝你好运!:)
void SafteyInspector::CheckProductSaftey()
{
  database.getProducts( list_of_products )

  foreach( list_of_products as p )
  {
    if ( !runBatteryOfTests( p ) )
      unsafe_products.insert( p );
  }

  if ( !unsafe_products.empty() )
  {
    recallDispatcher.recall( unsafe_products );
  }
}