Dependency Injection: Separating the Call Graph from the Construction Graph
I'm trying to exercise the principles of Dependency Injection, and I'm having some difficulty doing so.
I have a function that periodically goes off to my database, retrieves a list of products, and then runs a battery of tests against those products in order to determine their safety.
If one or more of the products is found to be unsafe, I need to issue a recall by instantiating and dispatching a ProductRecall
object.
The function looks something like this: (pseudo-code)
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() )
{
ProductRecall * recall =
new ProductRecall( unsafe_products ); // Oh no!
recall->dispatch();
}
}
The problem is that I'm "new"-ing a ProductRecall
object right in the middle of my call-graph. This violates the principles of Dependency Injection. As written, I can't test CheckProductSaftey()
without also creating a ProductRecall
object.
However, I can't pass the ProductRecall
object into my SafetyInspector
object, because the SafetyInspector
is the one who determines the list of unsafe products.
I'm using constructor-injection for everything, and I'd like to continue doing so. Note also that I may issue multiple ProductRecalls
at any time, so I can't necessarily just pass a single ProductRecall
object into the SafetyInspector
at construction.
Any suggestions? Thanks!
如果你对这篇文章有疑问,欢迎到本站 社区 发帖提问或使用手Q扫描下方二维码加群参与讨论,获取更多帮助。

评论(2)

I think you actually need to conjure up some sort of ProductRecallFactory
to pass in instead. It's fairly common for injection containers to support some sort of factory-style interface, or you may just wish to make your SafetyInspector container-aware.
EDIT: by "container-aware" I'm referring to implementing some interface along the lines of COM's IObjectWithSite
, so that your object can call back to its parent. It's a weaker form of dependency injection which partially undoes the inversion of control. If you're doing the dependency injection manually, by all means inject a factory object.

I think the problem may be with your implementation of ProductRecall
. Specifically, if you can call dispatch()
on a newly created object, it implies that a lot of actual functionality is either hidden inside the ProductRecall
class, or that the ProductRecall
class has static members and/or singletons to give it everything else it needs.
I would recommend creating a class called ProductRecallDispatcher
which handles the intricacies of an actual dispatch. You would then create one of these objects, and pass it to the constructor for SafteyInspector
. This would make your CheckProductSafety
function look as follows:
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 );
}
}
发布评论
需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。