google-code-export / google-guice

Automatically exported from code.google.com/p/google-guice
Apache License 2.0
2 stars 1 forks source link

explicit module dependencies via module injection #151

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
Currently there's no good way to find out which of a module's dependencies
should be provided by other modules.  Suppose we did something like this:

class MyModule extends InjectableModule {
  @Inject
  void inject(A a, B b, C c) {
   bind(A.class).to(a);
   bind(B.class).to(b);
   bind(C.class).to(c);
  }

  void configure() {
    ...

    export(D.class, E.class, F.class);
  }
}

Whenever attempting to satisfy a dependency for a binding in MyModule, it
would only use local bindings unless they are explicitly declared as a
module dependency.

Then we would have a clear specification of what a module provides and what
it requires from other modules, so it would be clearer when you're changing
a module in a backward-incompatible.  Also, each module would effectively
have its own private namespace, which might also help solve the robot arms
problem.

Original issue reported on code.google.com by bslesinsky on 7 Sep 2007 at 1:43

GoogleCodeExporter commented 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

GoogleCodeExporter commented 9 years ago
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:

GoogleCodeExporter commented 9 years ago
Interesting!  Can you talk about how you use GuiceBox in your application?

Original comment by bslesinsky on 14 Nov 2007 at 4:12

GoogleCodeExporter commented 9 years ago
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:

GoogleCodeExporter commented 9 years ago
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

GoogleCodeExporter commented 9 years ago
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

GoogleCodeExporter commented 9 years ago
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

GoogleCodeExporter commented 9 years ago
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

GoogleCodeExporter commented 9 years ago
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

GoogleCodeExporter commented 9 years ago
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

GoogleCodeExporter commented 9 years ago
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

GoogleCodeExporter commented 9 years ago
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