Injecting Those Dependencies 171
Last week I was teaching a class with a good group of C++ developers in Chicago. They were up on C++, current standardization efforts, virtual machines, performance analysis and tuning. At one point I mentioned a metric for virtual method dispatch on the JVM and one of the students used my numbers to work backwards to determine the number of instructions on different processors.
I was teaching our Working Effectively with Legacy Code class. Michael Feathers usually teaches this class, but he was busy (probably working with legacy code somewhere, which, ironically, is why he wrote the book – to solve the problem once and for all and to stop working with legacy code).
public void addEvent(Event event) {
event.added();
events.add(event);
mailService.sendMail("jacques@spg1.com", "Event Notification", event
.toString());
display.showEvent(event);
}
public Scheduler(String owner, SchedulerDisplay display) {
this.owner = owner;
mailService = MailService.getInstance();
this.display = new SchedulerDisplay();
}
The display object is associated with a real device (OK, not in our simulation, but you get the point). We need to fix that dependency.
So we traditionally talk about a few ways to address that:- Create an interface and then have a concrete and test-double implementation
- Create a test subclass
#pragma once
template<class D> class Scheduler
{
public:
Scheduler(D &display);
~Scheduler();
void handleEvent();
private:
D &display;
};
- Link seam: link in a different version of SchedulerDisplay
- Compiler seam: use a template parameter
- Dynamic seam: create a test subclass that removes external dependency
- Dynamic seam: extract an interface from existing concrete class, and make Scheduler depend on the new interface.
For C++, the first option might be a good option if it allows a very quick build/test cycle. Imagine using a class that brings in several unwanted dependencies. By making a test version replacement and linking to it, you might be able to get up and running quickly. On the other hand, you have to have a custom build target, which is really outside of the language.
The second option is good for C++ and maybe C#, but not Java. Java’s implementation of generics is so bad, that it requires an interface for this particular case, so you end up with the final option anyway. Using templates puts the seam into the language rather than the build system. It will affect clients because either:- They will have to provide a template parameter
- Or, by providing a default value for the template parameter, clients will be aware of a class they were not previously aware of. Still, a viable option, but it will increase build time at least a little.
The third option may not work in any language without changing the original problem code. Imagine a no argument constructor that performs some static initialization. Without changing the constructor, you cannot guarantee that this approach will work. In any case, the problem as presented requires a change since the constructor was doing the initialization of the display; it did not allow for dependency injection.
The final option requires a bit more change, but is pretty flexible if you can afford the virtual method overhead. In Java this isn’t much of an issue (for a number of reasons, mostly related to effective JIT optimization). The cost is a bit more of a problem in C++ because introducing a first virtual method is a big deal. Personally, I don’t work on embedded systems, so I can generally ignore that problem. But this is not something to simply ignore, context does matter.
So which option is the best?
None of the above.
As a developer, you should probably be aware of all of these options (or maybe not the template option of you’re strictly a Java developer). Then, when you are faced with a legacy coding problem, you’ll have more tools from which to choose.
Improving Testability of GUI Code, an Eample 82
Just finished first draft. More to come, questions/comments appreciated.
http://schuchert.wikispaces.com/tdd.Refactoring.UiExample