Injecting dependencies doesn't have to hurt
I have noticed a recent swell of comments about the pain of dependency injection, and if this has caused you problems on your project, then I think I can help you by offering a few simple guidelines and a single goal for injecting dependencies.
I have formulated these as Novice or Advanced Beginner rules, and so I have worded them more strongly than I tend to word my advice.
- When in doubt, inject each dependency directly into the method that requires it.
- Only when you inject the same dependency into multiple methods of the same class, move the parameter into the constructor.
- Do not use so-called “setter injection” except as an intermediate step in a refactoring aimed at injecting the dependency into a method or the constructor.
- Stop using the Service Locator pattern, and instead inject the service into the client.
- Stop instantiating collaborating services (in the Domain-Driven Design sense), and instead inject those services into the client.
- When a constructor parameter list becomes uncomfortably long, split the class so that the new classes’ constructor parameter lists don’t overlap.
- If you notice a class using the same dependency through two different object graph routes, split the class so that the new classes receive the dependency directly in their constructors.
- If you notice a class using groups of dependencies at different times, split the class so that the new classes only use each cohesive group of dependencies.
All these guidelines have their roots in “remove duplication” and “fix bad names”, two of the four elements of simple design.
So why inject dependencies at all?
Some people inject dependencies in order to stub, mock or spy on a method for testing. I used to use that as a primary motivation, but over the years that motivation has evolved into something more widely useful. I inject dependencies for one reason: to move the things that change in the direction of the client and the things that don’t in the direction of the supplier. Or, if you prefer, “abstractions in code, details in data”. Or, if you prefer, to avoid abstractions depending on details. Any of those will do.
You don’t buy it?
No problem. I’ll write more articles in this space showing examples of each of the guidelines, and you’ll have plenty of opportunity to share your opinions about them.