It depends on the intended lifetime and ownership of your objects. To construct an object of type C you need an object of type D. Shall this D object have the same lifetime as the C object? Then it makes sense to construct the D object at the same scope where the C object is constructed. Shall the D object live longer than C? Then you should construct it outside before and pass it to the function which constructs C.
Given your objects B, C and D shall have the same lifetime, and that lifetime shall be controlled by an object of type A, it makes sense to let A construct them and "wire" them. If the D object shall live longer, it must be constructed (and destroyed) outside of A.
If you note that A gets too much responsibilities by managing other, dependent objects of B, A might use a "BFactory" class for this purpose, like @gnat suggested as a response to your other question. And if you want to avoid A having to construct that factory, this factory could also be injected into A through an interface "IBFactory".
And if your system gets really large, because you have to not four but 400 classes to manage, then a DI container would be the better choice.
I meant to write this as a comment, but it turned out to be too long.
How I know that Foobar
is the only owner? In the old case it's simple. But the problem with DI, as I see it, is as it decouples class from construction of its dependencies, it also decouples it from ownership of those dependencies (as ownership is tied to construction). In garbage collected environments such as Java, that's not a problem. In C++, this is.
Whether you should use std::unique_ptr<Widget>
or std::shared_ptr<Widget>
, that is up to you to decide and comes from your functionality.
Let's assume you have a Utilities::Factory
, which is responsible for the creation of your blocks, such as Foobar
. Following the DI principle, you will need the Widget
instance, to inject it using Foobar
's constructor, meaning inside one of the Utilities::Factory
's method, for example createWidget(const std::vector<std::string>& params)
, you create the Widget and inject it into the Foobar
object.
Now you have a Utilities::Factory
method, which created the Widget
object. Does it mean, the method should me responsible for its deletion? Of course not. It is only there to make you the instance.
Let's imagine, you are developing an application, which will have multiple windows. Each window is represented using the Foobar
class, so in fact the Foobar
acts like a controller.
The controller will probably make use of some of your Widget
s and you have to ask yourself:
If I go on this specific window in my application, I will need these Widgets. Are these widgets shared among other application windows? If so, I shouldn't be probably creating them all over again, because they will always look the same, because they are shared.
std::shared_ptr<Widget>
is the way to go.
You also have an application window, where there is a Widget
specifically tied to this one window only, meaning it won't be displayed anywhere else. So if you close the window, you don't need the Widget
anywhere in your application anymore, or at least its instance.
That's where std::unique_ptr<Widget>
comes to claim its throne.
Update:
I don't really agree with @DominicMcDonnell, about the lifetime issue. Calling std::move
on std::unique_ptr
completely transfers the ownership, so even if you create an object A
in a method and pass it to another object B
as a dependency, the object B
will now be responsible for the resource of object A
and will correctly delete it, when object B
goes out of scope.
Best Answer
You're jumping steps. Consider a set of conventions optimized for loose coupling and exception safety. The rules go like this:
R1: if A contains a B, then the constructor of A receives a fully constructed B (i.e. not "B's construction dependencies"). Similarly, if B's construction requires a C, it will receive a C, not C's dependencies.
R2: if a full chain of objects are required to construct an object, the chained construction is extracted/automated within a factory (function or class).
Code (std::move calls omitted for simplicity):
In such a system, "who creates D" is irrelevant, because when you call make_b, you need a C, not a D.
Client code:
Here, D is created by the client code. Naturally, if this code is repeated more than once, you are free to extract it into a function (see R2 above):
There is a natural tendency to skip the definition of make_b (ignore R1), and write the code directly like this:
In this case, you have the following problems:
you have monolithic code; If you come to a situation in client code where you need to make a B from an existent C, you cannot use make_b. You will either need to write a new factory, or the definition of make_b, and all the client code using the old make_b.
Your view of dependencies is muddled when you look at the source: Now, by looking at the source you get to think that you need a D instance, when in fact you may just need a C.
Example:
struct A { B make_b(C c); }
will greatly increase coupling: now A needs to know the definitions of both B and C (instead of just C). You also have restrictions on any client code using A, B, C and D, imposed on your project because you skipped a step in the definition of a factory method (R1).TLDR: In short, do not pass the outermost dependency to a factory, but the closest ones. This makes your code robust, easily alterable, and renders the question you posed ("who creates D") into an irrelevant question for the implementation of make_b (because make_b no longer receives a D but a more immediate dependency - C - and this is injected as a parameter of make_b).