3

I'm new to dependency injection and though I've really liked it so far, I'm not sure where bindings should go. I'm using Guice in Java, so some of what I say might be specific to just Guice. As I see it, there's two options:

Accompanying the class(s) its needed for. Then, just write install(OtherClassModule.class) in whatever other modules want to be able to use said class. As I see it, the advantage of this is that classes that want to use it (or manage classes that want to use it) don't need to know any of the implementation detail. The issue I see is that what if two classes want to use two different versions of the same class? There's a lot of customization possible because of DI and this seems to restrict it a lot.

Implemented in the module of the class(s) its needed for. It's the flip of what I said above. Now you have customization, but not encapsulation.

Is there a third option? Am I misunderstanding something obvious? What's the best practice?

sinθ
  • 1,311

3 Answers3

4

Mark Seemann has written extensively about this topic. Here's a summary of his points.

  • For maximum reuse, structure your code as one or more libraries and/or frameworks which do not require or even mention a particular IoC container, and an application whose sole job is to create concrete instances and introduce them to each other (aka the Composition Root). A Composition Root may encapsulate an IoC container, or it may simply use new-expressions. Composition Roots are not meant to be reusable.

  • It should be straightforward to use a library or a framework without also installing an IoC container.

  • Use constructor injection to make libraries Dependency-Injection-friendly, though for convenience you can supply some creation methods which hard-code common dependencies. These creation methods should use new-expressions, not an IoC framework.

  • Use abstract factories to make frameworks Dependency-Injection friendly. For example, a web framework may need a way to create request handlers. By relying upon an interface for that operation, frameworks allow applications to customise the creation of handlers without requiring the use of a particular container.

So to answer your question directly: don't define any Guice bindings for your library. You shouldn't make your clients depend on a particular framework. If you want to provide a way to create objects with the "normal" set of dependencies filled-in, use new-expressions encapsulated inside creation methods or builders.

0

First of all, I assume this code is some kind of an API, because otherwise you would have known if there exist two classes that do want to use different implementations.

So, if you want a more coarse-grained API, go for the first alternative. If you want to let a programmer select implementation himself, go for the second alternative. Finally, if you want both, see the "robot legs" question in the Guice FAQ.

mkalkov
  • 311
-1

There's a third solution that I've been using instead.

Have the module accompany the class(s) it's needed for, but have multiple different versions (using inheritance in Guice) with different configurations. Then just install(...) whichever version I want. The advantage is that it's still encapsulated, but it is also customizable.

sinθ
  • 1,311