I’m writing a handy library (we’ll call it Thinger) that goes off and fetches an XML document, does some X-Path query on it and does something helpful with the result of that. (What I’m actually doing is far too boring to bother you all with, this is just a simplified example.)
Because I’m a wise and lazy programmer I’m making use of two third-party components, one for fetching remote XML documents (we’ll call it Fetcher) and one for running X-Path queries (we’ll call it Querier).
My library provides a Thinger class, into which one must inject a Fetcher and a Querier. I could do this via setters or a constructor.
But then I think about the poor people using my library, who now have to think about injecting my dependencies when all they really want to do is call something like
new Fetcher()->fetch(someurl);
So far, I have written a static factory which instantiates a new Thinger, Fetcher and Querier, assembles them as appropriate and returns the Thinger.
That left me feeling slightly unclean.
Surely there’s a better way?
EDIT
Most frameworks etc would probably get around this using a Dependency Injection Container or similar, but since I’m writing a fairly small third party library that seems like a bad approach.
As Dependency Inversion seems to get more and more popular, I would go for a dual approach.
- Design your components to be used with a Dependency Injection Container, if possible for use with an arbitrary one so you don’t constrain your users in their choice.
- As a convenience option, also provide a factory(-method) for creating a
Thinger
with the defaultFetcher
andQuerier
.
1
What’s wrong with providing a nice clean default implementation for the common scenario while allowing an injected version for people who want that flexibility?
As long as your default implementation doesn’t put undo constraints on usage (like manually deleting the Fetcher
and Querier
) or your default implementation is only common for the minority, that seems perfectly fine.
Use an inversion of control container. In .NET this includes Castle Windsor or NInject
You can just specify which components you need in the constructor, and you can get an object like this
IThinger thinger = container.Resolve<IThinger>();
thinger.DoTheStuff();
The code above doesn’t care about which implementation of IKing you have, what dependency must be injected, etc.
It doesn’t need a lot of setup or complicated configuration. Normally, you need to write only one method or one file where you register all your services (like Thinger, Fetcher) and initialize the container with that registration
1