I was musing today about some things I’ve seen in Clojure and their relationship to dependency injection in Java, etc. I wanted to draw the connection and then see what people in the Clojure community thought about it.
I’ve used dependency injection in Java for a long, long time both with and without DI frameworks. I use them to make dependencies explicit and to manage how and from where code gets those dependencies, which subsequently makes code easier to reuse and easier to test.
For example, if I’ve got some class that needs a source of timestamps, I might create a TimeSource interface and an implementation of TimeSource that uses System.currentTimeMillis() and pass it to the class in the constructor instead of just calling System.currentTimeMillis() directly in the class. In the past, I’ve used such techniques to unit test annoying time-dependent code (for example code that has timeouts) by mocking the TimeSource so that I can “control time”. JSR 310, the forthcoming Date and Time API, has this concept as well.
In general, as I’ve written Clojure code I have no need for DI or at least in any way recognizable from a Java background. But I did recently see an analog to how DI is used above in Clojure. It’s not uncommon though to see a Clojure variable defined as *time-source* and a set of functions that make use of the time-source. And you then might choose the time-source impl to use with a macro that binds *time-source* to a particular instance with something like (with-time-source) or requires a set! on the thread-local binding for those variables.
It seems like the code in those functions is relying on an external resource which is being injected into a well-known variable instead of through the function signature. That feels a little to me like a Java class that depends on a resource obtained through some well-known lookup mechanism (whether that might be a static factory method or a service locator, etc) instead of by using dependency injection.
I’m a little conflicted about whether this seems good or bad. It seems like it’s the same kind of code that I would rewrite with dependency injection in Java for all the usual reasons. But in Clojure you aren’t calling static state and you are still perfectly capable of changing the implementation used for that resource at will, so those downsides of static calls in Java don’t seem to apply.
Am I capturing this Clojure idiom properly? Is this case analogous to what I describe in Java or is not? Why are the issues in Java not issues in Clojure?