How to create recursive graphs of objects using Guice?

Suppose I have 5 classes A, B, C, D, E that implement the common interface X. Each of the classes B, C, D, and E has a field of type X (therefore, they can be thought of as shell classes).

Which instances are created is determined at runtime, so I could have, for example, one of the following object graphs:

E -> D -> C -> B -> A D -> B -> A E -> A A 

(The order is fixed and the innermost instance is always of type A, but otherwise there are no restrictions.)

No. I would like to create these objects using Guice to avoid providing all other dependencies manually.

What is the easiest way to do this? Currently it seems that I should

  • Let Guice instantiate A
  • create a module that binds X.class to this instance and a child injector to this add-on module
  • Let Guice create the next instance (e.g. type B)
  • repeat 2., now binding X.class to instance B
  • repeat 3. and 4. until all objects are created.

Is there an easier way? Can I somehow automatically register a new instance of subclass X as a binding for X every time it is created?

Edit: Clarification: "What instances are created at runtime":

My current code is as follows:

 X createX() { X x = new A(dependencies); if (useB) x = new B(x, some, dependencies); if (useC) x = new C(x, further, dependencies); if (useD) x = new D(x, dependency); if (useE) x = new E(x, yet, other, dependencies); return x; } 

The value of the flags useB, useC, useD and useE comes from the properties file.

My main goal is to keep all the dependencies on the designers manually.

Edit: Solution:

I added my own solution, which I found in the meantime. Thanks to all the defendants!

To improve my solution, you could remove the @InInstance annotation in the constructor options. I experimented with listener types, but I did not find a way to do this. Hints are welcome.

+4
source share
3 answers

I developed the following solution to the problem:

First, I create an @InInstance marker annotation that takes a Class object as a value. I use this annotation to annotate type X parameters in all wrapper classes (e.g. B to E). Example:

 class B { B(@InInstance(B.class) X x, Other dependencies) { ... } } 

Now, if I want, for example, an instance of a complete chain (A to E), I bind

  • X.class to E.class
  • X.class is annotated using InInstance (E.class) to the D.class class
  • ...
  • X.class annotated with InInstance (B.class) in A.class

and then call createInstance(X.class) .

This solves the problem of providing Guice for creating my instances and providing dependencies.

Now for the problem of creating the appropriate bindings at runtime according to the properties file, I created the Guice extension, which allows me to create “wrapper bindings”. Such a binding takes the type (here X), the implementation type of the shell (here one of B in E), and the base module, which should contain the binding for X. Then it creates a new module that contains two bindings:

  • same binding for X as in base module, but annotatedWith(InInstance(wrapper type))
  • binding from X to wrapper type

Such a module can then be used to redefine the base module bindings using Modules.override(base module).with(wrapper module) .

With some nice Guice-style EDSL style, it looks like this:

 Module baseModule = ... // contains binding for X to A if (useB) { Module wrapperModule = new ChainingModule() { void configure() { wrap(X.class).from(baseModule).with(B.class); // possible to use .in(...) etc. here // In this case, this line is equivalent to // bind(X.class).annotatedWith(InInstance(B.class)).to(A.class); // bind(X.class).to(B.class); // It has the advantage of automatically looking up the current binding // for X in the base module, which makes it easy to chain this. } } baseModule = Modules.override(baseModule).with(wrapperModule); } ... 

And with some helper methods for ordinary cases, it looks like this:

 Module module = ... // contains binding for X to A if (useB) { module = Chaining.wrap(X.class).from(module).with(B.class); } if (useC) { module = Chaining.wrap(X.class).from(module).with(C.class); } if (useD) { module = Chaining.wrap(X.class).from(module).with(D.class); } if (useE) { module = Chaining.wrap(X.class).from(module).with(E.class); } Injector injector = Guice.createInjector(module); X x = injector.createInstance(X.class); 

Each line creates a corresponding wrapper module and returns a new module containing the given module, with some bindings redefined by the wrapper module.

+4
source

"In Runtime" adds quite a bit of complexity. I assume that this means that you create an instance every X, you make some decision about the chain of wrapping objects A.

How about if you don't enter X into E, D, C, and B during construction? Instead, add the setNext (X) method. I'm not sure how realistic for you, but listen to me. You can create a factory as follows:

 class XFactory { @Inject Provider<A> a; @Inject Provider<B> b; @Inject Provider<C> c; @Inject Provider<D> d; @Inject Provider<E> e; public X create(Data state) { // the 'state' object is just whatever information you use to // decide what objects you need to create; X result = a.get(); if(state.useB()) { B head = b.get(); head.setNext(result); result = head; } if(state.useC()) { C head = c.get(); head.setNext(result); result = head; } if(state.useD()) { D head = d.get(); head.setNext(result); result = head; } if(state.useE()) { E head = e.get(); head.setNext(result); result = head; } return result; } } 

You might consider introducing an interface, such as DelegatingX or WrapperX, to store the setNext () method. This can reduce some repetitions.

Also, if "run-time" is actually loading the application, and therefore each instance of X must be the same chain of objects, then you have an additional option:

 class MyModule extends AbstractModule { public void configure() { Multibinder<DelegatingX> chain = Multibinder.newSetBinder(binder(), DelegatingX.class); if(useB()) chain.addBinding().to(B.class); if(useC()) chain.addBinding().to(C.class); if(useD()) chain.addBinding().to(D.class); if(useE()) chain.addBinding().to(E.class); } @Provides X provideX(Set<DelegatingX> chain, A a) { X result = a; // 'item' is B, then C... for(X item : chain) { item.setNext(result); result = item; } return result; // this is E } } 

I know this answer is pretty abstract, but I hope it brings you closer to a solution.

+1
source

I also found that the run-time definition part is fuzzy.

But let's say we start by imagining how these chains are made without Guice.

 class chainHolder { public final X chain1 = new E(new D(new C(new B(new A())))); public final X chain2 = new D(new B(new A())); public final X chain3 = new E(new A()); public final X chain4 = new A(); } 

But your problem is that there are many other things that need to be passed to each constructor that you would like to introduce? Ok, let an injection be given instead:

 //Pseudo multi-class def here classes B...E extends X { private final X nextInChain; @Inject public B...E(TheStuffINeedToo NotMentionedAbove, X nextInChain) { super(TheStuffINeedToo); this.nextInChain = nextInChain; } } class A extends X { @Inject public A(TheStuffINeedToo NotMentionedAbove) { super(TheStuffINeedToo); } } class chainHolder { public final X chain1; public final X chain2; public final X chain3; public final X chain4; @Inject public chainHolder(@Chain1 X c1, @Chain2 X c2, @Chain3 X c3, @Chain4 X c4) { chain1 = c1; chain2 = c2; chain3 = c3; chain4 = c4; } } class chainModlule extends AbstractModule { public void configure() { //The other stuff you wanted provided gets bound here. bind(TheStuffINeedToo.class).to(TheStuffNotMentionedInTheQuestion.class); //Maybe A should always be the same terminating instance. bind(A).in(Singleton.class); bind(X).annotatedWith(Chain4.class).to(A.class); } @Provides @Chain1 public X providesChain1X(@Chain1 Provider<E> e) { return e.get(); } @Provides @Chain1 public E providesChain1E(@Chain1 Provider<D> d, TheStuffINeedToo stuff) { return new E(stuff, d.get()); } @Provides @Chain1 public D providesChain1D(@Chain1 Provider<C> c, TheStuffINeedToo stuff) { return new D(stuff, c.get()); } @Provides @Chain1 public C providesChain1C(@Chain1 Provider<B> b, TheStuffINeedToo stuff) { return new C(stuff, b.get()); } @Provides @Chain1 public B providesChain1B(Provider<A> a, TheStuffINeedToo stuff) { return new B(stuff, a.get()); } // ... Similarly for chain2 then ... @Provides @Chain3 public X providesChain3X(@Chain3 Provider<E> e) { return e.get(); } @Provides @Chain3 public E providesChain3E(Provider<A> a, TheStuffINeedToo stuff) { return new E(stuff, a.get()); } } 

Now. You see that it does not save / a lot of work. If all classes accept the same thing that you wanted to do for you (for example, my pseudo-customization), you can flip the entire chain to one provider and reuse this material instance or provider for this material.

+1
source

All Articles