Question
What criteria should be used when deciding between:
- specifying a dependency with an annotation, and
- specifying a dependency with a more specific interface
Example
Suppose I have:
interface FooLoader {
Foo loadById(long id);
}
class DBFooLoader implements FooLoader {
... jdbc etc. etc. ...
}
class CachingFooLoader implements FooLoader {
...
@Inject
public CachingFooLoader(FooLoader delegate) {
this.delegate = delegate;
}
...
}
Suppose I want to bind FooLoader
to CachingFooLoader
, I have [at least] two ways to wire this:
Use an annotation binding
Change:
public CachingFooLoader(FooLoader delegate)
to:
public CachingFooLoader(@NonCaching FooLoader delegate)
and then:
bind(FooLoader.class).annotatedWith(NonCaching.class).to(DBFooLoader.class);
Create a more specific interface
Change:
public CachingFooLoader(FooLoader delegate)
to:
public CachingFooLoader(NonCachingFooLoader delegate)
where NonCachingFooLoader
simply extends FooLoader
, and then have DBFooLoader
implement NonCachingFooLoader
, and wire up accordingly.
My thoughts
I am drawn to using an annotation binding for multiple reasons:
- Keys can be more easily reused than interfaces, which decreases the combinatorial explosion that interfaces would suffer from.
- It is less invasive: configuration stays in Guice modules, rather than "poisoning" classes.
However, creating a more specific interface has its advantages too:
- Interfaces have more meaning. Typically only Guice will read the annotation, where as interfaces are used for much more.
So, what criteria should be used to determine which approach to take?
(Spring users, as far as I can tell, this is what you guys call qualifiers.)