I've been reading recently about DI and IoC in C++. I am a little confused (even after reading related questions here on SO) and was hoping for some clarification.
It seems to me that being familiar with the STL and Boost leads to use of dependency injection quite a bit. For example, let's say I made a function that found the mean of a range of numbers:
template <typename Iter>
double mean(Iter first, Iter last)
{
double sum = 0;
size_t number = 0;
while (first != last)
{
sum += *(first++);
++number;
}
return sum/number;
};
Is this (i.e., using iterators instead of accessing the collection itself) dependency injection? Inversion of control? Neither?
Let's look at another example. We have a class:
class Dice
{
public:
typedef boost::mt19937 Engine;
Dice(int num_dice, Engine& rng) : n_(num_dice), eng_(rng) {}
int roll()
{
int sum = 0;
for (int i = 0; i < num_dice; ++i)
sum += boost::uniform_int<>(1,6)(eng_);
return sum;
}
private:
Engine& eng_;
int n_;
};
This seems like dependency injection. But is it inversion of control?
Also, if I'm missing something, can someone help me out? This seems to be the natural way to do things, so if that's all there is to Dependency Injection, why do people have a hard time using it?
Inversion of Control is a very generic concept, with different meanings depending on the sort of "control" you're talking about. Dependency injection is a specific form.
Inversion of Control and iteration
In this case "control" means "flow control".
I think your first example involving iteration is not really inversion of control, because that code explicitly does the flow control. Inversion of control would separate the action to perform from the flow control. It might look like this (pardon my java/C#):
SumVisitor sumVisitor = new SumVisitor();
collection.AcceptVisitor(sumVisitor);
int sum = sumVisitor.GetSum();
The visitor object does something for each collection element it visits, e.g. update a sum counter field. But it has no control over how or when it is called by the collection, hence inversion of control. You could also implement a MedianVisitor
, MeanVisitor
, MaximumVisitor
, etcetera. Each one implements a generic IVisitor
interface with a Visit(Element)
method.
For the collection, the opposite is true: it has no knowledge about what the visitor does and simply takes care of the flow control by calling visitor.Visit(element)
for each element in the collection. Different visitor implementations all look the same for the collection.
Inversion of Control and object graph construction
In this case "control" means "control over how components are created and wired together".
In any non-trivial application, code is split into components which have to collaborate. To keep the components reusable, they cannot directly create each other as that would permanently glue them together. Instead, individual components give up control over construction and component wiring.
Dependency injection is one way to achieve this, by taking references to collaborator objects in the constructor. You then need a separate piece of start-up code where all the components are created and wired together, or a dependency injection framework that takes care of this for you. Your Dice class is indeed an example of dependency injection.
Another way to give up control over object graph construction is the Service Locator pattern, though it has its disadvantages.
Let me try to answer.
Your first example is neither. It is simply a template.
For it to be dependency injection, an IMPLEMENTATION would have to have been chosen, and provided to the template.
For it to be IoC, the template would have to be provided at runtime (not compile time) to the IMPLEMENTATION type, and used as the implementation of the "mean()" function (think of a factory that provides mean function implementations)
Your second example looks like a consumer of DI/IoC. The code that sends the implementation of Engine into your class would be the DI/IoC component.
Hopefully, that is accurate, and helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With