electrotype
electrotype

Reputation: 8816

Guice : How to customize the bindings of a third-party Module?

Let's say I have a ThirPartyModule third-party Module which binds lots of components that I can then use in my application :

Injector guice = Guice.createInjector(new MyAppModule(), new ThirPartyModule());

If I want to modify the implementation classes used for some bindings in that Module, what is the best approach?

For example, let's say ThirPartyModule performs that binding :

bind(WidgetInterface.class).to(DefaultWidgeImpl.class).in(Scopes.SINGLETON);

and I want to be able to change the DefaultWidgeImpl class for MyWidgetImpl class. I know I could use an overriding Module, and simply rebind the WidgetInterface key. But what if ThirPartyModule binds a lot of things using that same Widget implementation? I may not want to have to rebind each of them!

So I'm trying to find the best solution to be able to specify the implementation class to use, without having the rebind all the components depending on it.

I guess ThirPartyModule could first create a getter method for the implementation class :

bind(WidgetInterface.class).to(getWidgetImpClass()).in(Scopes.SINGLETON);

protected Class<? extends WidgetInterface> getWidgetImpClass() {
    return DefaultWidgeImpl.class;
}

and then the application could override the getWidgetImpClass() method :

Injector guice = Guice.createInjector(new MyAppModule(), new ThirPartyModule() {
    @Override
    protected Class<? extends WidgetInterface> getWidgetImpClass() {
        return MyWidgetImpl.class;
    }   
});

I also though about passing the implementation class to the constructor of the Module :

Injector guice = Guice.createInjector(new MyAppModule(), new ThirPartyModule(MyWidgetImpl.class));

I'd like to know if there is an accepted pattern to customize such third-party Modules? Let's say I can ask the Modules to be written in a specific way if it helps them to be customizable.

Upvotes: 1

Views: 588

Answers (1)

Tavian Barnes
Tavian Barnes

Reputation: 12932

Here's how I would do it:

public class ThirdPartyModule extends AbstractModule {
    @Override
    protected void configure() {
        // CoolWidget --
        //              \
        //               > WidgetInterface -> DefaultWidgetImpl
        //              /
        // AwesomeWidget

        OptionalBinder.newOptionalBinder(binder(), WidgetInterface.class)
                .setDefault()
                .to(DefaultWidgetImpl.class);

        bind(CoolWidget.class).to(WidgetInterface.class);
        bind(AwesomeWidget.class).to(WidgetInterface.class);
        // etc.
    }
}

public class MyAppModule extends AbstractModule {
    @Override
    protected void configure() {
        OptionalBinder.newOptionalBinder(binder(), WidgetInterface.class)
                .setBinding()
                .to(CustomWidgetImpl.class);
    }
}

By making all the bindings go indirectly through the WidgetInterface key, you only need to override that one binding.

Upvotes: 2

Related Questions