use Module overrides for decorator pattern

416 views
Skip to first unread message

Mike Grove

unread,
Mar 19, 2014, 12:20:06 PM3/19/14
to google-guice
I'd like to have some default implementation of an interface, and then in a separate module, provide a new implementation which uses the old one, via the decorator pattern.  And everywhere that should have gotten the default, gets the overridden one, and the modified behavior.

I can get the override to work, but getting the original implementation injected into the override/decorator creates a circular dependency (not surprisingly).  I thought that maybe using an annotation in the overridding module might get around it, but then I think we'd need the annotation *everywhere* we want that implementation, and that just gets me back to the circular dependency.

Any ideas on how to accomplish this?

Thanks

Mike

public class TestModuleOverride {

public static void main(String[] args) {
Module base = new AbstractModule() {
@Override
protected void configure() {
bind(A.class);
}
};

Module override = new AbstractModule() {
@Override
protected void configure() {
bind(A.class).to(B.class);
}
};

Injector i = Guice.createInjector(Modules.override(base).with(override));

System.err.println(i.getInstance(A.class).doSomething());
}

public static class A {
public String toString() { return "A";}

public String doSomething() { return "A is doing something"; }
}

public static class B extends A {
private A a;
@Inject
public B(final A theA) { a = theA; }

public String toString() { return "B" + a;}

public String doSomething() { return "B is doing something, " + a.doSomething(); }
}
}

Philipp Wendler

unread,
Mar 19, 2014, 1:32:49 PM3/19/14
to google...@googlegroups.com
Hello,

Am 19.03.2014 17:20, schrieb Mike Grove:
> I'd like to have some default implementation of an interface, and then in a
> separate module, provide a new implementation which uses the old one, via
> the decorator pattern. And everywhere that should have gotten the default,
> gets the overridden one, and the modified behavior.

I had the same problem, and developed a solution that I described here:
http://stackoverflow.com/q/7416974/396730

Hope this helps you.

Philipp

Mike Grove

unread,
Mar 19, 2014, 1:37:08 PM3/19/14
to google-guice
Looks promising.  Is the source for your annotation & utilities available anywhere?

Thanks.

Mike
 


Hope this helps you.

Philipp

--
You received this message because you are subscribed to the Google Groups "google-guice" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-guice...@googlegroups.com.
To post to this group, send email to google...@googlegroups.com.
Visit this group at http://groups.google.com/group/google-guice.
For more options, visit https://groups.google.com/d/optout.

Philipp Wendler

unread,
Mar 19, 2014, 2:02:15 PM3/19/14
to google...@googlegroups.com
Hi,

Am 19.03.2014 18:37, schrieb Mike Grove:
> On Wed, Mar 19, 2014 at 1:32 PM, Philipp Wendler <m...@philippwendler.de>wrote:
>> Am 19.03.2014 17:20, schrieb Mike Grove:
>>> I'd like to have some default implementation of an interface, and then
>> in a
>>> separate module, provide a new implementation which uses the old one, via
>>> the decorator pattern. And everywhere that should have gotten the
>> default,
>>> gets the overridden one, and the modified behavior.
>>
>> I had the same problem, and developed a solution that I described here:
>> http://stackoverflow.com/q/7416974/396730
>
>
> Looks promising. Is the source for your annotation & utilities available
> anywhere?

So far no.
I ended up not using it for other reasons, and so I never added tests
etc. and made it available online. I could do so, but it would need some
time I guess.

If you have a hard-coded chain of decorators, you could probably leave
out the most complicated part (which is the overriding of bindings) and
just declare the bindings by hand:
A.class annotatedWith(InInstance(B.class)) to A.class
A.class to B.class

Maybe you need to use a common interface for A and B that is used as the
actual binding, not sure if the above works directly. With an interface
it looks like this:
I.class annotatedWith(InInstance(B.class)) to A.class
I.class to B.class

The InInstance annotation does not contain anything big, it is just a
regular annotation (you can take a look at the implementation of Named).

Greetings,
Philipp

Mike Grove

unread,
Mar 19, 2014, 2:37:58 PM3/19/14
to google-guice
Actually, this.  Doing the obvious modification to my example, leaving A as the base class and B extending it with no interface common to the two did not work.  When adding an interface I, which is common to both, seemed to do the trick.  Otherwise, I still got the circular binding exception.

This should work for me, i think, for my current use case.

I appreciate the pointer.

Below is my final code example which illustrates it working for future reference.

Cheers,

Mike

public class TestModuleOverride {

public static void main(String[] args) {
Module base = new AbstractModule() {
@Override
protected void configure() {
bind(I.class).to(A.class);
}
};

Module override = new AbstractModule() {
@Override
protected void configure() {
bind(I.class).annotatedWith(Foo.class).to(A.class);
bind(I.class).to(B.class);
}
};

Injector i = Guice.createInjector(Modules.override(base).with(override));

System.err.println(i.getInstance(I.class).doSomething());
}

public static interface I {
public String doSomething();
}

public static class A implements I {
public String toString() { return "A";}

public String doSomething() { return "A is doing something"; }
}

public static class B implements I {
private I a;
@Inject
public B(final @Foo I theA) { a = theA; }

public String toString() { return "B" + a;}

public String doSomething() { return "B is doing something, " + a.doSomething(); }
}

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@BindingAnnotation
public @interface Foo {
}
}

 

The InInstance annotation does not contain anything big, it is just a
regular annotation (you can take a look at the implementation of Named).

Greetings,
Philipp

Tim Boudreau

unread,
Apr 1, 2014, 4:16:09 AM4/1/14
to google...@googlegroups.com
The "simplest thing that can possibly work" approach would be

@ImplementedBy(DefaultA.class)
public static interface A { ... }
private static class DefaultA { ... }

and then you have a binding for A that is 'overridden" if a module explicitly binds it.

If you're in a situation where it seems like something fancier is needed - say, you want half the implementation of the default A, but a few methods handled by B, then that's a good indication that you have a design problem - a class which is doing orthagonal things and ought to be split up. Probably a better solution would be to inject an object into A that does the behaviors you want to vary, or get rid of A altogether and have two types.

-Tim

Tim Boudreau

unread,
Apr 1, 2014, 4:17:07 AM4/1/14
to google...@googlegroups.com
On Tuesday, April 1, 2014 4:16:09 AM UTC-4, Tim Boudreau wrote:
The "simplest thing that can possibly work" approach would be

@ImplementedBy(DefaultA.class)
public static interface A { ... }
private static class DefaultA { ... }

Erm, make that
private static class DefaultA implements A{ ... } 
Reply all
Reply to author
Forward
0 new messages