During my learning about dependency injection (and acquiring first practical experience) I was wondering about one problem that occurred to me thinking about one concrete project that I'd like to tackle with DI in the near future.
For different analyses I'd like to create objects of an injected dependency dynamically, for I'd need an arbitrary number of them, which may vary due to users interactions with my program. I thought about implementing this requirement as an abstract prototype pattern
public interface IAnalysis
{
SomeDataType DoSomething();
IAnalysis CreateObject();
}
Classes derived from IAnalysis will be responsible for returning a new object of that class from CreateObject()
. Dependent classes can create new objects without knowing the concrete type, but only rely on the interface, hence a major concept of DI is complied to. Anyway, classes derived from IAnalysis will have to create new objects with the new
keyword. I read that creating objects with new
should be avoided outside the injector when using DI, thus I am not quite sure if this is "allowed" in DI. On the other hand this seems like a quite sensible solution to me, for the classes only create new objects of themselves, which actually should not hurt the DI principle.
Is the concept I thought of sensible? Are there any other solutions I can use to achieve this? I actually thought about abstract factories, but this would hurt the DI principle to my understanding.
I read that creating objects with
new
should be avoided outside the injector when using DI […].
This is only partially true. I will show you, step by step, that new
has its place, and that it might be just fine to use new
to implement your prototype pattern.
Let's start by stating the obvious: If we need an instance of type B
, then it has to be created by someone, somewhere. Let's say we have this:
class C
{
void Baz()
{
B b = new B(new A(…));
b.Bar();
}
}
Baz
requires a B
in order to do its work. If we want to avoid new B(…)
, the best we can do is remove it from this particular place in the code base:
class C
{
C(Func<B> newB) // instead of Func<B>, we could also inject a B directly
{ // (the difference being that we would no longer control
this.newB = newB; // when the B gets created)
}
Func<B> newB;
void Baz()
{
var b = newB();
b.Bar();
}
}
But the B
being passed to C
's constructor still has to be created somewhere. Only now it's somewhere else.
So what have we gained by avoiding the new
? C
is no longer required to have internal knowledge of how exactly to create a B
.
But how would Func<B> newB
(i.e. a factory method) itself create a B
without using new
? It seems we cannot shy away from new
forever.
To drive this point home, let's proceed to another, very related example that is a little closer to your issue (implementing the prototype pattern in a DI context): Abstract factories, another design pattern. Let's say that we have a BFactory
whose sole responsibility is to create instances of type B
:
interface BFactory
{
B CreateB();
}
Can we implement this without the use of new
? Let's try in the same fashion as above:
class RedundantBFactory : BFactory
{
RedundantBFactory(Func<B> newB)
{
this.newB = newB;
}
Func<B> newB;
public B CreateB()
{
return newB();
}
}
This would be absolutely pointless! The whole raison d'être of a factory is that it encapsulates knowledge about how to create instances of some type. Just because we wanted to avoid using new
in our factory, we've externalized exactly that knowledge, rendering the factory as a whole completely redundant (because it just forwards its own main responsibility to another party, which has to do the equivalent work)!
We can conclude that it is reasonable and appropriate to use new
inside abstract factories and factory methods (such as in BFactory
or even newB
above), if we don't want them to be completely redundant:
class UsefulBFactory : BFactory
{
public UsefulAFactory(Func<A> newA)
{
this.newA = newA;
}
Func<A> newA;
public B CreateB()
{
return new B(newA());
}
}
Now to your prototype pattern: The prototype pattern is essentially about object cloning. That is, all types that implement your IAnalysis
interface must be able to create clones (copies) of an instance. Just as with the abstract factory example above, the sole purpose of your interface is to encapsulate some form of object creation. This is its reason to exist in the first place, so classes that implement this interface must not delegate away that responsibility to an external party. Again, it's completely reasonable to use new
in this case:
class W : IAnalysis
{
W(X x, Y y, …)
{
this.x = x;
this.y = y;
…
}
public IAnalysis CreateObject()
{
return new W(x, y, …);
}
}
A final remark, just to underline and complete my initial claim that avoiding new
doesn't make sense in all cases: Take note that DI shouldn't be used for everything, anyway.
Usually, you have to make a decision about which types should be handled by the DI container. These so-called dependencies, or components, or services are usually abstracted away as an interface
or abstract class BaseClass
, so that you could possibly substitute one implementation for another later on. The only place where you use new Service(…)
should be in the composition root, or (as shown above) in abstract factories or factory methods (which are themselves dependencies that will get injected to where you need to create objects at a time of your choosing). If you had new Service(…)
sprinkled liberally all over your code base, it would be difficult to replace one implementation with another.
But it is perfectly OK to use new
to create primitive values and instances of value types (such as string
, TimeSpan
, etc.). These types are usually not instantiated by the DI container.