Our second weekend challenge was to build a program that allows a customer to order a takeaway. The customer needed to be able to view a menu, add items off the menu to their basket, view a summary of their basket and checkout their order.
Pretty exciting, huh? For the first time, it seemed as though we were building something that had actual real-life application. This fact alone made the build much more intuitive – I am
all too reasonably aware of the processes involved in ordering a takeaway, which made the planning and diagramming stage way less traumatic. We all had an immediate sense of which parts needed to talk to others.
One of the most valuable techniques I took away from the takeaway is that of dependency injection.
I first came across the term reading Ruby newbie bible Practical Object-Oriented Design in Ruby by Sandi Metz (POO Dr for short *snigger*), in which Sandi muses:
Using dependency injection to shape code relies on your ability to recognise that the responsibility for knowing the name of a class and the responsibility for knowing the name of a message to send to that class may belong in different objects.
At first, this meant absolutely nothing to me. Literally. I had zero emotional response to the statement. However, digging deeper into the concept over the last week, it seems as though it’s sort of a big deal (and the internet is full of people who have inexplicably strong emotional responses to the subject…)
What is dependency injection?
Dependency injection may sound super technical/slightly medical, but in fact is a pretty straightforward beast to tame. Instead of having objects creating their dependencies, you inject dependencies straight into their lifeblood.
Imagine a Takeaway class existing as such:
In the end, it’s going to behave a little like a user interface and should allow us to access menu items via a print method. But the Takeaway class itself is not responsible for the menu – this has been delegated to a separate Menu class.
Taking this into consideration, Takeaway is heavily dependent on Menu in this first set up. In Metz’ words: not only does Takeaway know the name of another class (Menu) but it knows the name of a method of another class (print_out).
This is not ideal. What if the menu object changes? Or multiple menus are needed? Removing dependencies allows classes to be smarter and more adaptable by knowing less about their neighbours.
In the following example, a menu object will be automatically instantiated when Takeaway is initialised. Menu will be ‘injected’ by default:
This may look deceptively similar to the first code snippet, but the difference is vital. If, for example, you wanted to instantiate Takeaway with a different object inside, you can now very easily overwrite the initialisation with another class.
To illustrate, we could inject a dependency on an imaginary Cocktails class simply by calling Takeaway.new(cocktails). In this case, as long as the Cocktails class contains a method ‘print_out’ and responds to it in the expected way, we’re good to go!
By decoupling our classes, our code has become miles more flexible, extendable and less likely to be attacked by internet trolls.
Win, win and win.