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.
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.
Related
So I have some code that runs an algorithm, say with an AlgoRunner class. Now this AlgoRunner class can be implemented in multiple ways to run different algorithms using Algo classes. I want to use Dagger 2 to provide different implementations of the AlgoRunner class to a "Manager" class that passes input to AlgoRunner as well as other components that it manages.
Question
I have the following right now, but I'm not sure if this is the correct way, mainly because of that empty AlgoRunnerProvider module. Is that any other way to achieve what I'm trying to do? Or to simplify what I have?
Should I just create different components, OneAlgoRunnerComponent and TwoAlgoRunnerComponent and inject the Manager from each of those?
The class that constructs the Manager instance uses this component to inject the AlgoRunner into that instance so that the Manager can pass it the inputs.
#Component(
modules = {
AlgoRunnerProvider.class
}
)
public interface AlgoRunnerComponent {
void inject(Manager manager);
AlgoRunner getAlgoRunner();
}
AlgoRunnerProvider Module
#Module
public class AlgoRunnerProvider {
#Provides
public AlgoRunner getAlgoRunner() {
return null;
}
}
OneAlgoRunnerProvider, that overrides the provides method in AlgoRunnerProvider.
Could have a TwoAlgoRunnerProvider as well, that does the same thing and provides TwoAlgoRunner, as long as that extends AlgoRunner.
public class OneAlgoRunnerProvider extends AlgoRunnerProvider {
private final OneAlgo algo;
public OneAlgoRunnerProvider(OneAlgo algo) {
this.algo = algo;
}
#Override
public OneAlgoRunner getAlgoRunner() {
return new OneAlgoRunner(algo);
}
}
All this is used like this right now:
AlgoRunnerComponent build = DaggerAlgoRunnerComponent.builder()
.algoRunnerProvider(new OneAlgoRunnerProvider(new OneAlgo()))
// .algoRunnerProvider(new TwoAlgoRunnerProvider(new TwoAlgo()))
.build();
Manager manager = managerComponent.getManager();
build.inject(manager);
Truth.assertThat(manager.algoRunner).isInstanceOf(OneAlgoRunner.class);
// Truth.assertThat(manager.algoRunner).isInstanceOf(OneAlgoRunner.class);
Thanks a lot!
The dagger framework is used to handle object creation for you. If you start doing some sort of initialization in one of your classes you wish to provide, there is probably something not as it is supposed to be (see getAlgoRunner()).
If you have different types that you want to provide at runtime, you want a factory of some sorts to create the correct object. Enter dagger.
You have multiple ways of achieving what you want. Basically, the module should handle the object creation:
#Module
public class AlgoRunnerProvider {
#Provides
public AlgoRunner getAlgoRunner() {
// todo create the correct type
return null;
}
}
1. #Named annotation (or some other Qualifier)
If you know at compile time which class is going to need which type, you should use qualifiers.
#Named("Version1")
#Inject
AlgoRunner mRunner;
You then just can provide different implementations from your module:
#Provides
#Named("Version1")
public AlgoRunner getAlgoRunner() {
return new Version1AlgoRunner();
}
#Provides
#Named("OtherVersion")
public AlgoRunner getAlgoRunner(Depends someOtherDependency) {
return new OtherVersionAlgoRunner(someOtherDependency);
}
2. Switching at runtime
While you could always use the first option by creating multiple classes with different dependencies, you might want to be able to chose at runtime. For this, you need to pass in some argument to your module:
#Module
public class AlgoRunnerProvider {
private final int mType;
public AlgoRunnerProvider(int type) {
mType = type;
}
#Provides
public AlgoRunner getAlgoRunner() {
if(mType == TYPE_A) {
return new Version1AlgoRunner();
} else {
return new OtherVersionAlgoRunner();
}
}
}
With this variant you still have your creation logic inside your module, where your dependencies come from.
3. Use different modules
Another approach would be to use different modules. This will only be a clean solution if determined at compile time (different classes using different modules) and not some choosing logic at runtime in the same class.
If you start writing code like if typeA then moduleA else moduleB you should probably stop and do something else.
You can use the same component but create it using different modules for different classes by using good old inheritance. Every module just provides its implementation of AlgoRunner.
// one class using one module to get a specific behavior
public class MyClassVersionA {
public void onCreate(Bundle saved) {
component.myModule(new MyModuleVersionA()).build();
}
}
// another class using a different module to get a different behavior
public class MyClassSomethingElse {
public void onCreate(Bundle saved) {
component.myModule(new MyModuleSomethingElse()).build();
}
}
You would then just subclass your module accordingly like so
// did not test the following, something like this...
public abstract class MyModule {
#Provides
public AlgoRunner getAlgoRunner();
}
public class MyModuleVersionA extends MyModule {
#Provides
#Override
public AlgoRunner getAlgoRunner() {
return new Version1AlgoRunner();
}
}
public class MyModuleSomethingElse extends MyModule {
#Provides
#Override
public AlgoRunner getAlgoRunner() {
return new SomeOtherAlgoRunner();
}
}
There are probably even more possibilities, especially if starting to mix those approaches, but I think those 3 to be the basic tools that you can use.
Is it possible to instantiate and assign a singleton to a reference with Guice before creating the Module and pass that instance to the Module constructor be bound during configuration?
Here is an example of what I mean:
I have a method that allows me to create objects depending on a custom implementation of an interface which is being passed in constructor as an Optional (if the user won't provide a custom implementation, we will use the default one), which is being done by binding the interface to that particular implementation in the Module class. :
public static MyClass createMyClassObject(Optional<SpecialInterface> customSpecialInterfaceObject) {
SpecialInterface specialInterfacebject;
if(customSpecialInterfaceObject.isPresent() {
specialInterfaceObject = customSpecialInterfaceObject.get()
} else {
/* here I would like to bind it to an instance of the DefaultSpecialInterfaceObject but can't really do something like:
Injector injector = Guice.createInjector(myClassModule);
DefaultSpecialInterface instance = injector.getInstance(DefaultSpecialInterface.class);
as the module is yet to be created */
}
MyClassModule myClassModule = new MyClassModule(specialInterfaceObject);
Injector injector = Guice.createInjector(myClassModule);
return injector.getInstance(MyClass.class);
}
I'm currently using classes instead of instances to solve this problem, such as in the example below, but I don't quite like this solution. Would be happy to see a better way of doing it:
private static Class resolveSpecialInterfaceObject(Optional<SpecialInterface> customSpecialInterfaceObject) {
Class specialInterfaceObjectClass;
if (customSpecialInterfaceObject.isPresent()) {
specialInterfaceObjectClass= customSpecialInterfaceObject.get().getClass();
} else {
specialInterfaceObjectClass = DefaultSpecialInterface.class;
}
return specialInterfaceObjectClass;
}
public abstract class MyClassModule extends AbstractModule {
private final Class<SpecialInterface> specialInterfaceObjectClass;
public MyClassModule(Class<SpecialInterface> specialInterfaceObjectClass) {
this.specialInterfaceObjectClass= specialIntefaceObjectClass;
}
#Override
protected void configure() {
bind(SpecialInterface.class).to(specialInterfaceObjectClass);
}
}
Edit, from a comment below:
one more thing- didn't want to make the question too long; actually, I also want to perform another operation on the resulting instance of SpecialInterface, but only if it is the instance of DefaultSpecialInterface and I don't think it should be done in the Module. I was thinking if I could just have this bean up and running before, such as in Spring, so I could just pass it to the Module, but also use it in another method call before?
Can you take the whole Optional and use bind(...).toInstance(...)?
public static MyClass createMyClassObject(
Optional<SpecialInterface> customSpecialInterfaceObject) {
MyClassModule myClassModule = new MyClassModule(customSpecialInterfaceObject);
Injector injector = Guice.createInjector(myClassModule);
MyClassFactory instance = injector.getInstance(MyClassFactory.class);
return instance.createMyClassObject();
}
class MyClassModule extends AbstractModule {
private final Optional<SpecialInterface> customObject;
MyClassModule(Optional<SpecialInterface> customObject) {
this.customObject = customObject;
}
#Override public void configure() {
if (customObject.isPresent()) {
// Singleton by necessity: Guice doesn't know how to create another one.
bind(SpecialInterface.class).toInstance(customObject.get());
} else {
// Default scoped. Add ".in(Singleton.class)" if necessary.
bind(SpecialInterface.class).toInstance(DefaultSpecialInterfaceClass.class);
}
}
}
If you want to perform additional initialization on DefaultSpecialInterface and nothing else, you have a number of options:
If some kind of initialization is important for all implementations and likely too heavy to put into a class constructor, add an initialize method on your SpecialInterface. Make the custom one a no-op, and implement it for DefaultSpecialInterface.
If the initialization is unique to DefaultSpecialInterface, I see no reason why it shouldn't be in the Module. Write a #Provides method or bind to a Provider<SpecialInterface> that creates and initializes DefaultSpecialInterface correctly.
If your real goal is to keep the business logic out of a Module, you can do so by extracting it into a free-standing Provider or DefaultSpecialInterfaceFactory that is responsible for that.
Remember, Guice is responsible for feeding fully-constructed objects into your object graph, and that means that injecting a SpecialInterface should get a ready-to-use implementor of the SpecialInterface general contract. If Guice needs to perform some initialization to make that happen, it's not unreasonable to have it do so, and a Module isn't a bad place to do it.
Suppose I have a value for which I have a default, which can be overridden if System.getProperty("foo") is set. I have one module for which I have
bindConstant().annotatedWith(Names.named("Default foo")).to(defaultValue);
I'm wondering what the best way of implementing a module for which I want to bind something annotated with "foo" to System.getProperty("foo"), or, if it does not exist, the "Default foo" binding.
I've thought of a simple module like so:
public class SimpleIfBlockModule extends AbstractModule {
#Override
public void configure() {
requireBinding(Key.get(String.class, Names.named("Default foo")));
if (System.getProperties().containsKey("foo")) {
bindConstant().annotatedWith(Names.named("foo")).to(System.getProperty("foo"));
} else {
bind(String.class).annotatedWith(Names.named("foo")).to(Key.get(String.class, Names.named("Default foo")));
}
}
}
I've also considered creating a "system property module" like so:
public class SystemPropertyModule extends PrivateModule {
#Override
public void configure() {
Names.bindProperties(binder(), System.getProperties());
if (System.getProperties().contains("foo")) {
expose(String.class).annotatedWith(Names.named("foo"));
}
}
}
And using SystemPropertyModule to create an injector that a third module, which does the binding of "foo". Both of these seem to have their downsides, so I'm wondering if there is anything I should be doing differently. I was hoping for something that's both injector-free and reasonably generalizable to multiple "foo" attributes. Any ideas?
Your first design seems like the best option to me if you don't need the binding to change at runtime (i.e. the binding is constant as of injector creation time).
For any value you decide at runtime, you'll need a Provider or a #Provides method:
public class SimpleIfBlockModule extends AbstractModule {
#Override public void configure() { }
#Provides #Named("foo") String provideFoo(
#Named("Default foo") String defaultFoo,
AnyInjectableDependencyHere ifNeeded) {
if (System.getProperties().containsKey("foo")) {
return System.getProperty("foo");
} else {
return defaultFoo;
}
}
}
If you need to decide at runtime based on a parameter, use this solution.
I have the following classes:
public class CacheModule extends AbstractModule {
#Override
protected void configure() {
bindConstant().annotatedWith(Names.named(TIMEOUT")).to(60);
// ...etc.
}
}
public class DefaultCacheAdaptor implements CacheAdaptor {
private CacheModule bootstrapper = new CacheModule();
#Named("TIMEOUT") private int timeout;
// other fields omitted for brevity
public DefaultCacheAdaptor() {
super();
Injector injector = Guice.createInjector(bootstrapper);
#Named("TIMEOUT") int t = injector.getInstance(Integer.class);
setTimeout(t);
}
}
public class QueueModule extennds AbstractModule {
#Override
public void configure() {
bind(CacheAdaptor.class).to(DefaultCacheAdaptor.class);
}
}
public class DefaultQueueAdaptor implements QueueAdaptor {
private QueueModule bootstrapper = new QueueModule();
private CacheAdaptor cacheAdaptor;
public DefaultQueueAdaptor() {
super();
Injector injector = Guice.createInjector(bootstrapper);
setCacheAdaptor(injector.getInstance(CacheAdaptor.class));
}
}
The CacheModule/CacheAdaptor/DefaultCacheAdaptor is located in a different JAR than QueueModule/QueueAdaptor/DefaultQueueAdaptor, and so the latter JAR depends on the former JAR at runtime (obviously).
The purpose of coding things this way is to allow the CacheModule to boostrap/inject the entire object graph under DefaultCacheAdaptor when the user writes:
CacheAdaptor cacheAdaptor = new DefaultCacheAdaptor();
Ditto for the QueueAdaptor.
It just so happens to be that the QueueAdaptor gets injected with a CacheAdaptor.
However, DefaultCacheAdaptor is the "root" of its own object tree, and should always be injected by the CacheModule.
So I ask: how can I bind DefaultCacheAdaptor to CacheAdaptor from inside QueueModule, but ensure that the DefaultCacheAdaptor is itself initialized/bootstrapped by the CacheModule?
To be honest it sounds like this problem isn't actually one of Guice, but instead the standard problem across software engineering: ensuring your dependencies do what they claim to do. QueueModule shouldn't concern itself about whether DefaultCacheAdaptor holds up to its general contract. Instead, write a unit test for DefaultCacheAdaptor that guarantees that it bootstraps itself, then use it in QueueModule without a second thought.
This is especially true because DefaultCacheAdaptor has a completely unrelated injector tree. You should be able to use DefaultCacheAdaptor opaquely, and stop concerning QueueAdaptor with its implementation details. Its bootstrapping is part of its implementation, not its API.
Even if you were to merge the two Injector graphs into one (by injecting the Injector and calling createChildInjector, for instance), there's very little way to guarantee at compile time that bindings you need in other modules will exist, because Modules work at runtime. Your best bet is to write a unit test. You can fail a little faster by calling requireBinding, which will fail at Injector creation if that particular dependency doesn't end up satisfied externally.
A question about Guice. I'm still learning it, but I can understand the fundamentals.
This question was already asked a couple of times on the net, but never with a concrete answer(none that I could find).
Say I have a situation like on the picture(a similar example was somewere on the net).
public class Dog {}
public class Walk implements Walkable {
private final Dog dog;
private final boolean leash;
#Inject
public Walk(Dog dog, #Assisted boolean leash) {
this.dog = dog;
this.leash = leash;
}
public void go() {
}
}
public interface Walkable {
void go();
}
public interface WalkFactory {
Walk create(boolean leash);
}
public class AssistedMain {
public static void main(String[] args) {
Injector i = Guice.createInjector(new AbstractModule() {
protected void configure() {
install(new FactoryModuleBuilder().
implement(Walkable.class, Walk.class).
build(WalkFactory.class));
}
});
Walk walk = i.getInstance(WalkFactory.class).create(true);
}
}
That's all great. But the question is - can I, somehow, reinject that object instance to the "container"(injector) to be used on the classes that rely on this dependency.
So, lets add a interface Person, class PersonImpl.
The new classes source are:
public interface Person {
void walkDog();
}
public class PersonImpl implements Person {
private Walkable walkable;
#Inject
public PersonImpl(Walkable walkable) {
this.walkable = walkable;
}
public void setWalkable(Walkable walkable) {
this.walkable = walkable;
}
public void walkDog() {
walkable.go();
}
}
So, the question is - am I, somehow able to actually inject this particular instance into the added object. This is a simple example, but we can presume there are 10 levels of classes below this one.
The solution I found is not very flexible. Something like:
Injector i = Guice.createInjector(new SimpleModule(false, dog));
And then bind to concrete instance. That's not very dynamic. Basically, every time I need a different runtime/dynamic parameter I have to recreate the injector.
The Provider<T> is nice, the FactoryModuleBuilder helps, but how can I inject the objects back?
Are there more dynamic solutions to this problem?
Thanks.
MPierce - agreed. Ill try to explain the way i visualized the problem(you can correct me if im wrong).
Being originaly derived from a "service locator" pattern, the idea that it can manage more than services is optimistic to say the least.
We could split the application into Service and Data classes, or you could say that we have application and infrastructure code - "Dependency Injection", a great book.
So, basicly, dependecy injection, and dependency injection frameworks in general are great. For solving infrastructure, or "service" code.
Any dynamic(runtime) parameters being injected into the Container/Injector are basicly forcing you to end the object graph.
For example, we have the folowing design:
EmailMessage is a runtime parameter. It can be "injected" into email service outside the Container/Injector, but it ends the object graph. If we want to request EmailDispatcher, after we injected the EmailMessage into EmailService(which is, I repeat, done outside injector), we could no longer fetch EmailDispatcher from the injector.
Then, you could redesign your model so it "fits" into the Container/Injector concept of dynamic parameters.
But then again, you forced the design, and suddenly, EmailDispatcher has too many responsibilites. It could be used in such a context, where you dont have many infrastructure classes.
And when you have a design like you have in the third example picture, you cannot use the Injector/Container to fetch you a NextService3 instance(nor any below the level of EmailDispatcher).
The problem being - if you have any dynamic(runtime) parameters, you can only use dependency injection for classes above the class that requires a dynamic parameter, you can forget the classes below.
Phew.
Correct?
Part of the problem depends on how you're resolving that 'false' is the thing you want to set for the leash field. Is that coming from config data or what?
A provider method may be helpful...
class FooModule extends AbstractModule {
...
#Provides
Walkable getWalkable(Dog dog) {
boolean leash = getBooleanFromSomewhere();
return new Walk(dog, leash);
}
}
If you can clarify where that boolean is coming from, it'll help me to understand what type of approach is applicable.
You can use custom scopes, much like when using guice servlets. That way you can create your instance, and then seed it in the injector.