So, for most of you, Dependency Injection sounds like something you would associate with drug addiction. I assure you, this is not the case at all. At least not the Dependency Injection I’m talking about.No! Dependency Injection is the programming practice of passing the dependencies of your class or function explicitly, rather than instantiating them, or grabbing within the implementation details of that class, or function.
By injecting the dependency, rather than creating it yourself, you gain two things right off the bat. Firstly, you make any collaborators and dependencies explicit for a casual reader of your code. In a nutshell, we’re not hiding anything from the users of our code. Secondly, and perhaps infinitely more important, you make your code MUCH easier to test in isolation! OMG! I know right?!
How so? Well, if you use resource dependencies (eg. file systems, database connections, etc.), your code requires REAL resources to test. This is often far from ideal. However, if you can inject those dependencies, then we can create fakes of each resource and use those instead and not break any of our obligations. As it turns out, we can do the same thing with regular classes and interfaces as well. Pretty cool, huh?
Now, I’ve been familiar with the concept for quite some time. In fact, I’ve used it extensively through out my career without even thinking twice about it. However, I’m also guilty of instantiating my dependencies at construction time, or worse yet, at call time of a method, or function.
On the surface, this practice might seem harmless and if you’re not working on a large system, it more than likely is pretty innocuous. However, recently I’ve been working on a system that has become a behemoth of code. The problem it solves is as complicated as the solution I have created to solve it!
Unfortunately, I am now caught in a classic problem that plagues many, many programmers. Even some much more experienced than I! That is the butterfly effect of minor changes. Simply put: If I change something in one place, it creates bugs in other places. To add insult to injury, we don’t have a test suite in place for this code, so there is no real way for me to ensure that changes I make don’t have a trickle down effect. There are way too many classes to effectively test all of them. Rather, I must simply trust that the change I make is good, test it manually, on a handful of common problem areas, then commit the change and hope there is no trickle.
As it turns out, I am now in a position to rework, perhaps even completely rewrite this code base, as a business decision was made to split this subsystem out and make it a service our primary system will depend on. Fantastic!
So, in preparation for this, I started to look deeper into the Laravel framework. Laravel is a relative newcomer to the PHP framework stable. I’ve looked at CodeIgniter, as well as Kohana, which is a fork of CodeIgniter. I’ve looked at Zend, Cake and Symfony. Laravel was the one that really stood out because:
- It has a lot of the features I need in my system (eg. Routing, Queuing, ORM) rigged up and ready to go out of the box.
- It comes with an Inversion of Control (IoC) container built into it AND it makes liberal use of it within the framework plumbing and core code.
Inversion of Control? What’s that? Inversion of Control is basically the idea of making classes dependent on a low level class, or even an interface, rather than a concrete class. This allows you to make changes to the implementing classes, or even changing them out entirely, without fear of breaking anything!
An IoC container, a form of a Repository, is simply a design pattern that helps resolve classes and handle some of the dependencies those classes have making Dependency Injection MUCH easier.
I’ve been thinking about ways to solve the specific problem of adding unit tests to my existing code base, but for some reason the solution always escaped me until just a few weeks ago when I was reading the documentation for Laravel.
For the first time in almost a year, I’m excited to get back to work on this project. I have a clear view moving forward and I have solid ideas as to how to overcome a lot of the issues that have plagued this project since the day I took it over almost two years.
It’s funny how something that I’ve known about for literally years has suddenly found a new place in my stable of tricks. Really, this is something that should have been a part of my design consciousness for quite a while. Such a simple and obvious technique that in hind sight, would have solved a lot of problems I’ve run into over the years.
It just goes to show, no matter how long you’ve been doing something, and I’ve been doing this almost 12 years now, there is still plenty to learn if you would only make yourself willing to learn.