Dependency Injections (DI) means that a class is receiving its dependencies during runtime. Why is it useful? Because it gives us the flexibility to create different kinds of objects during runtime, using smaller models, without re-writing our code. Imagine it is like Lego - when you have many small pieces, you can build more versatile creations than with fewer big blocks. Of course on the other hand, if you have too many small blocks, you would just work harder to reach the same result. How small should our pieces be? A good principle to rely on is the single responsibility principle. If a class implements only one responsibility It will become more understandable, and easier to reuse by other classes.
Let's first view the following example.
public class Garden
{
private IList<IFlower> myFlowers;
public Garden()
{
myFlowers = new List<IFlower>();
myFlowers.Add(new Rose());
myFlowers.Add(new Lily());
}
// some flowers care logic
}
It's a pretty simple class, but it has two responsibilities - to create and to take care of flowers.
We cannot change the type of the flowers the garden is initialized with in this constructor, unless we do the following change:
We delegate the creation responsibility outside of the class using DI (the dependency is injected via the constructor):
public class Garden
{
private List<IFlower> myFlowers;
public Garden(List<IFlower> flowers)
{
myFlowers = flowers;
}
// some flowers care logic
}
What did it give us? It gave us the flexibility to construct different flower gardens during runtime. Now we can do this using simple "Injection" of different implementations of flowers into the garden:
public Garden CreateGardenBySeason(Season season)
{
switch(season)
{
case(Season.Winter):
myGarden = null;
break;
case(Season.Summer):
myGarden = new Garden(new List<IFlower> {new Sunflower()});
break;
case(Season.Fall):
myGarden = new Garden(new List<IFlower> {new Rose()});
break;
case(Season.Spring):
myGarden = new Garden(new List<IFlower> {new Orchid(), new Tulip()});
break;
}
}
That is basic dependency injection. Since we delegated the creation responsibility outside of the class, we can construct any flower garden we want and we can also test the flower care logic in Garden more easily using a mock:
public bool TestGarden()
{
// A fake flower that has no bugs because it has no logic!
IFlower fakeFlower = new FakeFlower();
Garden testGarden = new Garden(new List {fakeFlower });
// Run test ...
}
If Rose flower for example, had a bug, using the first implementation, our Garden test could have failed and we might have not known why. It might not seem like a big issue with these simple classes, but if the classes were bigger, more complex, and had many different dependencies, the bug would have been harder to spot.
Single Responsibility classes for creational logic
If we continue adhering to SRP, we would continue to asking where would we put the method "CreateGardenBySeason"? By SRP it should be in it's own class. A type of class that only has object creational logic is called, by convention, a factory (there is also the Factory design pattern for this purpose).
Where do we create the factories? Were do we create the classes? We can declare a class that is called context that is only responsible for the creation of "new" classes and factories for a scope of dependencies.
Now we stop and ask ourselves, should we really break our classes into such small pieces? If every class has a single responsibility it also means there would be many classes. The answer is - it depends on the requirements. In large projects, for the long run it is worth it. It will definitely be useful if classes are understandable, simple, modular and testable. In small projects with very few classes anyway - why bother? For some tiny project you can even get away with putting all the code in one class. Flexibility is needed when you might need it.
There is a lot more to say about the topic, however I tried to keep the focus on the "why", instead of the "how". I hope that I managed to convey the basic benefits of using DI. Thanks for reading!
Comments