As anyone who has looked at the code that I present on my blog knows, I tend to favour ‘prarameterise from above’ in my designs. That is code that doesn’t reach out to a global for something but that only works with what it has been given. This leads to you passing a fair number of references or pointers to your objects. In a later post, Jamie points out that he doesn’t like this ‘proliferation of pointers’ and says that for him they’d have to be ‘smart or shared’ pointers to even consider it. Just because you’re being explicit about passing all the required interfaces to your classes doesn’t mean that you are not being just as explicit about who owns these objects. Most of the ‘service’ interfaces that I pass to objects as part of their construction follow my ’no you can’t bloody delete me!’ pattern. They’re either references or, if optional or if required for delayed initialisation and you really can only pass a pointer, the interface has a protected destructor so it clearly states that ’no you can’t bloody delete me!’. There’s generally no need for smart pointers in this situation unless you’re confused about your object lifetimes (or they’re really complexly convoluted, which IMHO points you towards a rethink).
I don’t tend to use the service locator pattern as I prefer that each of my objects is explicit about which other things it uses; as Charles Nicholson says, far more eloquently than I can, here. This makes my testing easier as I can see that
Widget takes a reference to
IWrangleWidgets in its constructor and so when I test the code it’s obvious that I should cruft up a mock for the widget wrangler and pass that in. With a service locator I’d need to create the locator, add a widget wrangler to it and then let the widget obtain it from the locator. Apart from it being a long winded way of configuring things I dislike the implicit (or even hidden) nature of the configuration. I like to KNOW what my code uses by seeing it explicitly listed in the header file. I don’t want to have to go and dig into the implementation to see that someone has changed it to use an instance of
IJuggleMidgets as well. I also like the fact that ‘simple little changes’ (like accessing the midget juggler) cause a ripple effect out into the code that uses widgets. I know you probably all think me mad for this, but it’s a good thing. For a widget to suddenly require access to the midget juggler is a fairly large change and the further the ripples spread the larger the change it is; and the good thing is that it’s obvious and explicit. If the original design expected widgets to be able to juggle midgets then the ripples wont need to spread that far. If the original design did NOT expect this kind of interaction then you have some work to do to make it possible and the amount of work that you need to do might make you actually THINK about whether this is a good design change or not. With globals (or service locators) you’d just reach out from the widget and grab your midget juggler and be done with it; the change is easy even though it’s not intended to be easy and the easy change could now be causing major structural design rot.
So. Globals are evil.