In one of my previous posts, I wrote about ways that you can make life difficult when injecting dependencies. I find that systems where it is difficult to inject dependencies will be harder to change, will be harder to test, and will often be intimately coupled together in all sorts of interesting ways.
Some people (rightfully) commented that I should suggest some tips about what to do. I’d initially assumed it’d be easy to put a “Avoid if possible…” in front of each statement but I think it’d be more useful to describe some approaches to reducing the resistance. Here are a few tips:
- Tests in place – Find a seam, add some tests before starting to make changes. Work out what’s the best way you can evaluate whether or not you’re breaking behaviour in changing something.
- Remove static calls and state – Static functions tend to be easier to test as long as they’re not calling lots of other static methods, functions and ultimately state. Experiment to see if you can turn static items into instance methods without breaking anything. For those where the static function calls lots of other static functions, use a temporary refactor (ala Working Effectively with Legacy Code) to pass in its dependency via the method signature. It’s initially more messy but as long as you follow through on all changes and ultimately remove it (e.g. when you turn it into an object).
- See if you can “new” instances up in the constructor without breaking anything. Most calls can be harmless but sometimes they are not. If you can move the instantiation of a class to a constructor, then the next step is to pass it in via the constructor forcing anyone currently calling the method to instantiate it. Ultimately, you probably want something else to do it, but lets you isolate the class you’re investigating right now.
- Introduce roles instead of classes – I say roles because it’s easy to create interfaces that don’t mean anything. Try to use fine-grained roles and focus clients on using them instead.
- Migrate away from service location to constructor based dependency injection – Service location is often useful as a migration strategy when getting a system less coupled however the service locator pattern starts to fall short when you stop injecting services, and you start injecting objects with state (causing side effects across the system based on when a “service” is called). I prefer the more explicit constructor based dependency injection so that the side-effects, if any, will be made clearer and more obvious.
Hope this list was helpful. I’m not sure if it encompasses all of the things that I’m doing, but it’s a start.
0 Comments
2 Pingbacks