Overriding a module and reference a parent implementation

241 views
Skip to first unread message

Aurélien

unread,
Feb 3, 2015, 5:36:28 PM2/3/15
to google...@googlegroups.com
Hi,

I have a peculiar use case:
- I have 3 libraries, Core, Sub and OtherSub
- Sub and OtherSub do not know anything about each other, but they know the existence of Core
- Sub and OtherSub override the same functionality in Core
- In a project, I want to combine Sub and OtherSub.

It does not work, but here is how I wanted to implement this pattern with Guice:
- in the Core library:
    o bind(A.class).to(ACore.class)
    o class ACore implements A {
        @Override public String a() { "a"; }
    }
- in the Sub library:
    o bind(A.class).to(ASub.class)
    o class ASub implements A {
        private final A parent; // instance of ACore or AOtherSub
        @Inject ASub(A parent) { this.a = parent; }
        @Override public String a() { parent.a() + " overriden"; }
    }
- in the OtherSub library:
    o bind(A.class).to(AOtherSub.class)
    o class AOtherSub implements A {
        private final A parent; // instance of ACore or ASub
        @Inject ASub(A parent) { this.a = parent; }
        @Override public String a() { parent.a() + " !"; }
    }

then in the projet using this set of libraries, I would have been able to do:
- Guice.createInjector(Modules.override(new ModuleCore()).with(new AOtherSub())).getInstance(A.class).a() // returns "a !"
- Guice.createInjector(Modules.override(Modules.override(new ModuleCore()).with(new ASub())).with(new AOtherSub())).getInstance(A.class).a() // returns "a overriden !"
- Guice.createInjector(Modules.override(Modules.override(new ModuleCore()).with(new AOtherSub())).with(new ASub())).getInstance(A.class).a() // returns "a ! overriden"

In this implementation, this throws a StackOverflowError because in ASub or in AOtherSub the instance creation falls in an infinite loop.

Is there a way of doing something like that with Guice ?
Else do you have any insight on how I should implement this use case ?

Cheers,
Aurélien

Joshua Moore-Oliva

unread,
Feb 4, 2015, 10:59:09 AM2/4/15
to google...@googlegroups.com
If I am understanding your use case correctly, it sounds like you may be interested in using the OptionalBinder that is new in guice4. It would allow you to set up a default binding (setDefault) in core, and then override that in a sub (setBinding).

The javadoc is fairly thorough and has some examples here:

Stephan Classen

unread,
Feb 4, 2015, 4:42:46 PM2/4/15
to google...@googlegroups.com
One thing that will not work is injecting A into either ASub or AOtherSub. Because this will create an endless loop sinc A is bound to ASub resp. AOtherSub.
You need to be more specific and either request ACore or introduce a name for the parent so you could inject (@Parent A)

Aurélien

unread,
Feb 4, 2015, 5:34:09 PM2/4/15
to google...@googlegroups.com
Thank you for your responses.

The OptionalBinder lead me to what I want : a plugin-type architecture.
I could not reference a parent implementation with OptionalBinder.
However, the Multibinder enable to add multiple implementations and to reference all these implementations.

Although the Multibinder pattern is good, it means the Framework implementation needs to guess most of the sub plugins uses.

Here is an example of what I would like to do :
1/ The Framework defines a method to fetch the application users : List<User> users();
The base implementation connect to a database and fetch all the users.

2/ The plugin web-service fetch users on an API on the Internet and add them to the default implementation : list(parent.users()).addAll(ws.users());

3/ The plugin group filter users who are in the same group as the current user : parent.users().filter(user -> user.group() == currentUser.group());

Then in the application, I will be able to :
- use the framework only : users() returns all the users in the database,
- use the group plugin : users() returns all the users in the database who are in the same group as the current user
- use the web-service plugin : users() returns all the users in the database plus all the users provided by some web-service
- use both the group plugin and the web-service plugin : users() returns database users and web-service users but filtered by the group

This would be awesome because in the Framework I wouldn't have to think how plugins will change the default features.

To achieve this, using the MultiBindings Guice extension style, I would have framework and plugins declare modules this way :
public class ModuleGuice extends AbstractModule {
   
@Override protected void configure() {
       
Parentbinder
               
.newBinder(binder(), UserService.class)
               
.setBinding()
               
.to(UserFrameworkService.class);
   
}
}

Then, in the plugins service overriding, I would have something like :
public class UserWsService implements UserService {

   
private final UserService parent;
   
private final UserApi userApi;

   
@Inject public UserWsService(@Parent UserService parent, UserApi userApi) {
       
this.parent = parent;
       
this.userApi = userApi;
   
}

   
@Override public List<User> users() {
       
return ImmutableList.builder().addAll(parent.users()).addAll(ws.users()).build();
   
}

}

And eventually, in the Application, I would just have to do :
Injector injector = Guice.createInjector(new FrameworkModule(), new WebServiceModule(), new GroupModule());
injector
.getInstance(UserService.class).users(); // database + ws users filtered by group

Do I have to look in ExtendingGuice documentation to implement this or another class in the MultiBindings Guice extension might do the job (or elsewhere!) ?

Thank you for your help!
Aurélien

Stephan Classen

unread,
Feb 5, 2015, 3:00:39 AM2/5/15
to google...@googlegroups.com
So what you want is a chain like behavior where the order of the modules in the createInjector method determines the order in the chain.
I don't think this is easily possible with the current guice extensions. Multibinder offers a Set and a Map binder. Neither of them preserves the ordering.

You could make a hacky thing where you use Integer as key for the MapBinder. During injector creation you could have a static counter which will map each binding to a unique increasing value (0, 1, 2, ...). In the application you can then iterate over the number of elements in the Map and retrieve them by their key.

Something like this (sorry pseudo code, not my dev machine...):

----

private static int userServiceCounter;

public static addBindingToUserServiceChain(Binder binder, Class<? extends UserServiceFilter> implementation) {
    // make the binding here
}

----

class ChainedUserService implements UserService {

private final CoreUserService coreService
private final Map<Integer, UserServiceFilter> filterServices;

@Override
public List<User> getUsers() {
    final int n = services.size();
    List<User> result = coreService.getUsers();
   
    for (int i = 0; i < n; i++) {
        final UserServiceFilter filter = filterServices.get(i);
        result = filter.getUser(result);
    }
}

----

Core would then bind ChainedUserService to UserService.
--
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.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-guice/f348749b-8b33-4d81-a8c4-c5d1256805e7%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Aurélien

unread,
Feb 7, 2015, 12:10:14 PM2/7/15
to google...@googlegroups.com
Yes exactly: I want a chain like behavior where the order of the modules in the createInjector method determines the order in the chain.
However, I think the Multibinder Set binder does preserve the ordering. Indeed, Multibinder is using an ImmutableSet which preserves the order from construction time : https://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained

But ultimately, I also want to be able to create the Framework without thinking about how plugins will alter the base functionalities.
The ChainedUserService example have one main problem for me : code have to be added in the Framework to enable plugins to filter the result. So if a new plugin does not want to filter the result, but merge the result with another result (from a web-service for instance), the Framework will have to be modified in order to build the new plugin.

Looking at the code of the multibindings extension, it seems it isn't to hard to extend Guice. I will try to implement a POC of my use case and I will come back here to talk about it.

Thank you all for your answers, you enable me to understand my need better !

Philipp Wendler

unread,
Feb 7, 2015, 12:53:01 PM2/7/15
to google...@googlegroups.com
Hello,

Am 07.02.2015 um 18:10 schrieb Aurélien:
> Yes exactly: I want a chain like behavior where the order of the modules in
> the createInjector method determines the order in the chain.

Then maybe a solution I developed some time ago helps you:
http://stackoverflow.com/a/7544939/396730

It does allow you to create a chain of objects all implementing the
same interface, without dependencies between the individual pieces,
and configurable at runtime (during module construction).

Greetings,
Philipp

Stephan Classen

unread,
Feb 7, 2015, 3:02:17 PM2/7/15
to google...@googlegroups.com
Maybe filter was not the best choice of a name. So don't let yourself be tricket by this. At the end the interface method is:

List<User> getUsers(List<User> users);

And the concrete implementation is absolutely free to add, remove or replace users from the list.
Reply all
Reply to author
Forward
0 new messages