Inject what… in the Coffee Shop?
I believe if one would like to define an interface whose methods many people implement, it literally has to do with eating. Developers, in particular, are not an exception. The joy of coding in the coffee shop while sipping your double espresso is well known to many of us.
For those who have experienced it, getting a grasp of dependency injection in software development opens a new world to the developer. This is to the extent that once got it you are not going to code without it. Now you might ask what this has to do with the coffee shop? You will find that only after you see the dependency injection everywhere, even in your most joyful moments of having breakfast in the coffee shop… .
What do you order?
“Would you bring me breakfast with omelet as the main course and espresso for drink?” This is a sample order for breakfast in the coffee shop. To order, one needs to do the following:
Drink drink=new Espresso(money);
MainCourse mainCourse=new Omelet(money);
Breakfast breakfast=new YourBreakfast(drink,mainCourse);
The three interfaces used in the CoffeeShop class are Drink, MainCourse and Breakfast, whose implementations are in Espresso, Omelet and YourBreakfast classes per your particular breakfast order.
Now you are all set and waiting for your order to come, but this is the start of the story for the other side… .
First let’s assume the dependency injection is not used. The following picture illustrates the whole story behind the scenes then:
Alternatively, one may also design the above using Dependency Injection (DI). As a metaphor, suppose you are a regular of the coffee shop. Being regular, you may prefer to have the same breakfast every morning and not being asked for order each time. In other words, you don’t want to care about the details of your breakfast order everyday. You are only required to provide the dependency graph of your breakfast, being dependent on a drink as well as a main course, and instantiate them all by providing concrete examples for each, e.g. Espresso and Omelet, elsewhere in some class known as DI Container. This is what the Dependency Inversion Principle (DIP) implies, as illustrated below:
You would then only have to call the
serve method of
breakfast in case you want breakfast.
Does it make any difference which design to choose?
To answer this question, it would be a good idea to investigate the flexibility of the above designs to possible change in breakfast order. For example, you might want to change your drink to orange juice and main course to baked beans. Let’s see how the code now changes to reflect your current request.
The first case, i.e. without dependency injection, changes like this:
The red parts denote the parts of the diagram to be replaced with the green ones to suit the updated order.
In the second design, i.e. with dependency injection, however, there would be no change in the first part of the diagram. The second part, involving the DI container, changes like this:
The prominent advantage of the latter design using DI over the first one is that it leaves no trace on the composition root class of, where you invoke the
breakfast.serve() method. It is clear this is not true for the first case due to the instantiation being implementation in the same place as it is used. See the highlighted lines in the
Overall, even though extensibility is the main merit of using DI as well as most design and architectural patterns, there are indeed other benefits as well. These include memory tweak by using Singleton objects or scoping their creation and convenience in writing unit tests by mocking the objects whose creation is managed by the DI container.
There are other topics on dependency injection, such as Poor Man’s DI. I will try to explain them in subsequent posts if this one finds you well. So please leave your feedback, I would really appreciate it.