Quantcast
Channel: all and sundry
Viewing all articles
Browse latest Browse all 250

Disambiguating between instances with Google Guice

$
0
0
Google guice provides a neat way to select a target implementation if there are multiple implementations of an interface. My samples are based on an excellent article by Josh Long(@starbuxman) on a similar mechanism that Spring provides.

So, consider an interface called MarketPlace having two implementations, an AndroidMarketPlace and AppleMarketPlace:

interface MarketPlace {
}

class AppleMarketPlace implements MarketPlace {

@Override
public String toString() {
return "apple";
}
}

class GoogleMarketPlace implements MarketPlace {

@Override
public String toString() {
return "android";
}
}

and consider a user of these implementations:

class MarketPlaceUser {
private final MarketPlace marketPlace;

public MarketPlaceUser(MarketPlace marketPlace) {
System.out.println("MarketPlaceUser constructor called..");
this.marketPlace = marketPlace;
}

public String showMarketPlace() {
return this.marketPlace.toString();
}

}

A good way for MarketPlaceUser to disambiguate between these implementations is to use a guice feature called Binding Annotations. To make use of this feature, start by defining annotations for each of these implementations this way:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@BindingAnnotation
@interface Android {}

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@BindingAnnotation
@interface Ios {}

and inform the Guice binder about these annotations and the appropriate implementation corresponding to the annotation:

class MultipleInstancesModule extends AbstractModule {

@Override
protected void configure() {
bind(MarketPlace.class).annotatedWith(Ios.class).to(AppleMarketPlace.class).in(Scopes.SINGLETON);
bind(MarketPlace.class).annotatedWith(Android.class).to(GoogleMarketPlace.class).in(Scopes.SINGLETON);
bind(MarketPlaceUser.class).in(Scopes.SINGLETON);
}
}

Now, if MarketPlaceUser needs to use one or the other implementation, this is how the dependency can be injected in:

import com.google.inject.*;

class MarketPlaceUser {
private final MarketPlace marketPlace;

@Inject
public MarketPlaceUser(@Ios MarketPlace marketPlace) {
this.marketPlace = marketPlace;
}

}

This is very intuitive. If you have concerns about defining so many annotations, another approach could be to use @Named built-in Google Guice annotation, this way:

class MultipleInstancesModule extends AbstractModule {

@Override
protected void configure() {
bind(MarketPlace.class).annotatedWith(Names.named("ios")).to(AppleMarketPlace.class).in(Scopes.SINGLETON);
bind(MarketPlace.class).annotatedWith(Names.named("android")).to(GoogleMarketPlace.class).in(Scopes.SINGLETON);
bind(MarketPlaceUser.class).in(Scopes.SINGLETON);
}
}

and use it this way, where the dependency is required:

import com.google.inject.*;

class MarketPlaceUser {
private final MarketPlace marketPlace;

@Inject
public MarketPlaceUser(@Named("ios") MarketPlace marketPlace) {
this.marketPlace = marketPlace;
}

}

If you are interested in exploring this further, here is the Google guice sample and an equivalent sample using Spring framework

Viewing all articles
Browse latest Browse all 250

Trending Articles