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!
-
I think you actually need to conjure up some sort of
ProductRecallFactoryto 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.Runcible : Could you clarify what "container-aware" means? (I'm using Manual DI, if it makes a difference.) -
I think the problem may be with your implementation of
ProductRecall. Specifically, if you can calldispatch()on a newly created object, it implies that a lot of actual functionality is either hidden inside theProductRecallclass, or that theProductRecallclass has static members and/or singletons to give it everything else it needs.I would recommend creating a class called
ProductRecallDispatcherwhich handles the intricacies of an actual dispatch. You would then create one of these objects, and pass it to the constructor forSafteyInspector. This would make yourCheckProductSafetyfunction 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 ); } }Runcible : Hmm. You're definitely correct that there is quite a bit of complexity inside the ProductRecall object (and as you guessed, it does rely on a couple of singleton objects). That being said -- if I create a ProductRecallDispatcher class it seems like I'm going to have the same problem inside this new object. Specifically, I'll have to "new" a ProductRecall object from inside ProductRecallDispatcher::recall().e.James : It will depend on what the .recall() function actually does. I would suggest that most of that functionality should be moved into the dispatcher, and that a ProductRecall holds only the data associated with the actual recall, (and functions to aid in its creation). The "new"-ing would be less of a problem, since the RecallDispatcher would still own the recall object (perhaps by keeping an STL vector of pointers), and could safely dispose of it at destruction.e.James : Incidentally, those singletons can be a problem, since using them prevents you from doing proper dependency injection. http://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons/138012#138012e.James : Refactoring to remove singletons and switch to dependency injection is a fun job. Good luck! :)
0 comments:
Post a Comment