Reputation: 57
The following is a factory pattern that I wrote. But, in order to change it into a Provider, Guice documentation is not quite helping.
class ClientA extends AbstractClient {...}
class ClientB extends AbstractClient {...}
class ClientUtil {
private static AbstractClient client;
public static AbstractClient getClient(String key) {
ClientType clientType = ....
switch(clientType) {
case ".." : client = new ClientA.Builder()....build();
break;
case "..." :
default : client= new ClientB.Builder()....build();
}
return client;
}
}
class Application {
AbstractClient client = ClientUtil.getClient(key); // here, key is a string which is dynamic
}
Please provide some suggestions on how this can be written in a Provider format with Guice AssistedInject.
Upvotes: 2
Views: 2992
Reputation: 159
Assuming you want to create the Object directly and thus need to use @Assisted you can let Guice create named factories:
import javax.inject.Inject;
// com.google.inject:guice:4.2.2
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.name.Names;
// com.google.inject.extensions:guice-assistedinject:4.2.2
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.FactoryModuleBuilder;
public class StackOverflow {
public static void main(String[] args) {
final Injector injector = Guice.createInjector(new GuiceModule());
Key<ClientFactory> key = Key.get(ClientFactory.class, Names.named(args[0]));
System.out.println("Client: " + injector.getInstance(key).create("xxx"));
}
}
class GuiceModule extends AbstractModule {
@Override
protected void configure() {
install(new FactoryModuleBuilder().implement(AbstractClient.class, ClientA.class)
.build(Key.get(ClientFactory.class, Names.named("ClientA"))));
install(new FactoryModuleBuilder().implement(AbstractClient.class, ClientB.class)
.build(Key.get(ClientFactory.class, Names.named("ClientB"))));
}
}
abstract class AbstractClient {
}
class ClientA extends AbstractClient {
private String key;
@Inject
public ClientA(@Assisted String key) {
this.key = key;
}
@Override
public String toString() {
return "ClientA [key=" + key + "]";
}
}
class ClientB extends AbstractClient {
private String key;
private Injector injector; // just an example for additional injections
@Inject
public ClientB(@Assisted String key, Injector injector) {
this.key = key;
this.injector = injector;
}
@Override
public String toString() {
return "ClientB [key=" + key + "]";
}
}
interface ClientFactory {
AbstractClient create(String key);
}
The downside to this approach is that in order to use a dynamic input you need a reference to the Injector and Guice complains that this is very slow - that may not be an issue for you tough. If somebody knows how to replace the direct injector.getInstance call with something better please let me know!
Upvotes: 0
Reputation: 1460
First off, I would definitely agree with @Lesiak. The code client= new ClientB.Builder()....build();
isn't clear as the ellipsis could be any number of fields you're setting on ClientA/B.
But to give you an example of how to use AssistedInject for your particular instance:
class ClientA extends AbstractClient {
@Inject
public ClientA(ServiceOne serviceOne,
ServiceTwo serviceTwo,
@Assisted MyObject myObject) {
...
}
}
class ClientB extends AbstractClient {
// Same constructor as ClientA
}
Your factory would then look something like:
interface ClientFactory {
@Named("ClientA") public AbstractClient getClientA(...);
@Named("ClientB") public AbstractClient getClientB(...);
}
Your parameters can be different objects, or whatever you want, but they essentially have to matchup with the constructor @Assisted annotation. You can see now why @Lesiak provided the answer he did, if your builder is setting 10 fields on ClientA, then your factory method will need to have 10 method parameters, and is very unruly.
You'd then use this with:
@Inject ClientFactory clientFactory;
...
AbstractClient client = clientFactory.getClientA(something, something1, ...);
...
Upvotes: 1
Reputation: 25956
Did you try to write your Factory manually? The manual you linked has a nice example, and your code will translate easily to Guice.
public interface ClientFactory {
AbstractClient create(String key);
}
public class ClientFactoryImpl implements ClientFactory {
@Override
public AbstractClient create(String key) {
if ("A".equals(key)) {
return new ClientA();
} else {
return new ClientB();
}
}
}
and bind factory to implenetation
bind(ClientFactory.class).to(ClientFactoryImpl.class);
AssistedInject is detrimental in your case. All it offers is building ClientFactoryImpl automatically. The implementation only passes injected and assisted arguments to the constructor. But you have some non-trivial logic in the create method. In this case, I suggest you create factory implementation yourself.
Upvotes: 1