Open GoogleCodeExporter opened 9 years ago
I'd love for modules to be able to declare and export dependencies.
Original comment by limpbizkit
on 14 Sep 2007 at 1:58
I've been working on this, and have a class I call GuiceBox (attached) that
more or
less does it, with two caveats: You can only export one thing, and importing
is a
little bit awkward.
The way it works is pretty simple: GuiceBox is an abstract class that
implements
Module and Provider<T>; Provider#get is your export, and your constructor is
your
import. GuiceBox's (final) get method creates an injector, installs `this` as a
module, and returns injector.getInstance(...). To use it, you just bind it as a
Provider or (if you don't need the facade it provides) install it as a Module.
There are a couple more bells and whistles-- a helper method to facilitate
rebinding
injected dependencies into the internal module and the ability to merge in other
Modules to facilitate extension.
Initially I wanted to easily control exports, but I've found that being limited
to
the Provider#get method forces the use of a facade, which is really nice for
encapsulation and makes the application architecture a lot cleaner.
What I'd really like to do, though, is get access a Binder in GuiceBox's
constructor.
If I had that, you could do rebinding of dependencies right in the constructor
rather than assigning to a field and rebinding that field in the configure
method
(which is what the helper method does, more or less). It's just not possible
because
BinderImpl is package private and the Guice facade doesn't really give me a way
to
get at the Binder it instantiates.
Original comment by logan.jo...@gmail.com
on 14 Nov 2007 at 1:58
Attachments:
Interesting! Can you talk about how you use GuiceBox in your application?
Original comment by bslesinsky
on 14 Nov 2007 at 4:12
Sure. I'm a consultant, so I can't get too specific, but we're basically
building a
desktop application (for lack of a better term) to drive an industrial scanner.
The
application is composed of functional modules. Each module has a facade
interface,
and its implementation is encapsulated in a GuiceBox. The view and control
within a
module also have facades with implementations encapsulated in GuiceBoxes-- the
model
is a collection of dumb objects, so no such encapsulation is needed.
It looks something like the attached code. BlockB requires BlockA in order to
function, and the application requires BlockB (and maybe BlockA). This is kind
of a
simplified example, so it looks a lot like we're propagating dependencies-- I
promise
we're not, and that the facades are aggregates which make this worth doing.
Original comment by logan.jo...@gmail.com
on 14 Nov 2007 at 6:51
Attachments:
Every time you call get(), it builds another injector. I wonder if we could
avoid
that somehow?
Original comment by bslesinsky
on 15 Nov 2007 at 3:48
I don't see any reason not to just cache the Injector in a field. Build it in
the first call and reuse it afterward.
If the facade is bound in singleton scope then caching the injector will make
GuiceBox violate the "create
every time" contract of Provider#get. You'd have to check for that, or somehow
prevent it.
I'm more interested in getting access in the constructor to the same Binder
used in configure(). rebind() is
kind of a lame substitute for that.
Original comment by logan.jo...@gmail.com
on 15 Nov 2007 at 5:03
I haven't actually used GuiceBox yet, but I still think it's pretty promising.
I've
been fooling around with automatically generated functions and realized that
GuiceBox
might be better. Here are some ideas:
- It seems like using field injection instead of constructor injection will
remove
some boilerplate, assuming that a GuiceBox is only created via Guice. Instead of
rebind() calls, we could put @Rebind annotations on the fields and use
reflection to
figure out what to rebind. (Or maybe that should be called @BindLocally.)
- Perhaps types of these fields should be providers rather than instances to
avoid
eagerness?
- Instead of implementing Provider, perhaps GuiceBox could implement Injector
and
forward all the Injector methods to its internal injector, which is created on
first
use. Then you can call getProvider(Class<T>) to get each provider you want.
(That's
more general but I'm not sure it's better in practice. Implementing Provider
makes a
GuiceBox more function-like, while implementing Injector would make it more
like a
set of functions in the same closure.
Original comment by bslesinsky
on 25 Nov 2007 at 10:42
You can do field injection now, of course, and just do your rebindings in the
configure() method. My preference is to keep the rebindings and internal
bindings
separate, but that's just preference.
My only issue with @Rebind is that it's roughly equivalent to rebind(), and I
view
rebind() as a somewhat crippled workaround.
There's no reason you couldn't ask for Providers to be injected now (as fields
or in
the constructor), but if you want to do it conveniently in the constructor
you'd need
a rebindProvider()-type method. (See why I'd much rather just have access to
the
Binder in the constructor?)
I like the idea of implementing Injector, except for two things: actually using
getProvider() would be a bit unwieldy in practice, and Injector doesn't specify
which
types it can actually provide. I could probably get behind multiple-export
(even
though single-export is, as I've said, a useful constraint) if those two issues
were
addressed somehow. The former would probably need some addition to
LinkedBindingBuilder; the latter would seem to require some kind of Provider
factory
interface other than Injector. Type erasure is probably going to be a problem
though.
Original comment by logan.jo...@gmail.com
on 26 Nov 2007 at 12:05
Hmm... listing all your imports as fields still seems cleaner to me, but I'll
have to
try it.
Regarding the exports, I imagine subclassing AbstractModule, overriding bind(),
and
adding a toMultiProvider() method:
bind(MyGuiceBox.class).in(Scopes.SINGLETON);
bind(FirstType.class).toMultiProvider(MyGuiceBox.class);
bind(SecondType.class).toMultiProvider(MyGuiceBox.class);
Instead of implementing Injector, possibly that could be via a new interface:
interface MultiProvider {
<T> Provider<T> getProvider(Class<T> typeWanted);
}
Interesting that an Injector could be considered a MultiProvider.
Original comment by bslesinsky
on 26 Nov 2007 at 1:03
Yeah, I think fields would be cleaner if you added @Rebind.
LinkedBindingBuilder.toMultiProvider() is pretty much what I was thinking, only
I
hadn't gotten as far as pulling the getProvider() method off of Injector--
LinkedBindingBuilder.toInjector() is more what I had in mind.
Subclassing AbstractModule and overriding bind() is a colossal pain in the ass,
by
the way, because of the builder pattern used in binding and the Guice authors'
penchant for final and private members. It's hard, if not impossible, to avoid
writing a lot of brittle code that way. (I've been down this road a little bit
on
another issue.)
Can you think of any way to do something like this:
interface ProviderFactory<T> {
<T> Provider<T> getProvider(Class<? extends T> typeWanted);
}
public class MultiPF implements ProviderFactory<TypeA>, ProviderFactory<TypeB> {
}
... without getting screwed by type erasure, as MultiPF here is?
Original comment by logan.jo...@gmail.com
on 26 Nov 2007 at 1:29
Actually, I take that bit about overriding bind() back. You're right, you could
probably just return a decorator that wraps the appropriate binding builder and
forwards the methods you don't care about.
Original comment by logan.jo...@gmail.com
on 26 Nov 2007 at 1:32
The module dependencies problem is fixed with the new Binder.getProvider() API.
It allows you to ensure certain
dependencies are available at module execution time.
If you'd like to open another issue or enhancement for GuiceBox, please do! Or
post a tutorial or something on
the Guice-Users list.
Original comment by limpbizkit
on 14 May 2008 at 3:57
Original issue reported on code.google.com by
bslesinsky
on 7 Sep 2007 at 1:43