0

I'm new to using Dagger 2 and am wondering what its advantages are over a technique that I currently use to achieve dependency injection.

Currently to achieve DI I create a project with two flavors, mock and prod. Within these flavors I create a class called Injector.

//prod Injector
public class Injector {
    public static INetworkLayer provideNetworkLayer() {
        return new ProductionNetworkLayer();
    }
}

//mock Injector
public class Injector {
    public static INetworkLayer provideNetworkLayer() {
        return new MockNetworkLayer();
    }
}

The Injector class has a static method for each object I need to inject. In my application code I can then simply write:

INetworkLayer networkLayer = Injector.provideNetworkLayer();

This works great for testing because in my production code or test code I can simply tell the Injector what I want and depending on what build variant I am using, the Injector will give me either the production object or the mocked test object. If I want a certain object to be a singleton I am simply able to hold a reference to it in the Injector class and then give that reference out when provide...() is called.

I have started using Dagger2 in a new project and have found that it is much more confusing to setup mock / prod dependencies compared to the "Injector" class method that I demonstrated above.

How is what Dagger 2 does different than the "Injector class" method?

Is there a good way to use Dagger 2 to provide mock and prod classes?

neonDion
  • 2,278
  • 2
  • 20
  • 39
  • Your way is more like Service Locator. I wrote an [article](http://bayou.io/draft/In_Defense_of_Service_Locator.html) comparing SL and DI, in which I also mentioned link time freedom of swapping dependencies. – ZhongYu Nov 22 '16 at 22:05
  • checkout https://stackoverflow.com/questions/26939340/how-do-you-override-a-module-dependency-in-a-unit-test-with-dagger-2-0 – David Rawson Nov 22 '16 at 22:17

1 Answers1

4

am wondering what its advantages are over a technique that I currently use to achieve dependency injection.

Automatic dependency graph resolution, and scoped providers via adding a scope annotation.


But your example can easily be converted into one that can be used with Dagger2, look:

//prod Injector
@Module
public class NetworkModule {
    @Provides
    @Singleton
    public static INetworkLayer provideNetworkLayer() {
        return new ProductionNetworkLayer();
    }
}

//mock Injector
@Module
public class NetworkModule {
    @Provides
    @Singleton
    public static INetworkLayer provideNetworkLayer() {
        return new MockNetworkLayer();
    }
}

And

@Singleton
@Component(modules={NetworkModule.class})
public interface SingletonComponent {
    INetworkLayer provideNetworkLayer();
}

And

public class Injector {
    private Injector() {
    }

    private static SingletonComponent singletonComponent;

    static {
        singletonComponent = DaggerSingletonComponent.create();
    }

    public static SingletonComponent get() {
        return singletonComponent;
    }
}

Because then you can do:

INetworkLayer networkLayer = Injector.get().provideNetworkLayer();


Now you can ask, "wait a second, I already did that but now it takes more code to set up!"

And that's because you never really showed what happens in case your dependencies depend on each other.

For example,

@Module
public class NetworkModule {
    @Provides
    @Singleton
    public static INetworkLayer provideNetworkLayer(OkHttpClient okHttpClient) {
        return new ProductionNetworkLayer(okHttpClient);
    }

    @Provides
    @Singleton
    public OkHttpClient provideOkHttpClient() {
        return new /*create okHttpClient*/;
    }
}

And also, you can actually use @Inject annotation to simplify your modules to a certain degree. Although when using interfaces, it's not as evident.

@Singleton
public class ProductionNetworkLayer {
    private OkHttpClient okHttpClient;

    @Inject
    public ProductionNetworkLayer(OkHttpClient okHttpClient) {
         this.okHttpClient = okHttpClient;
    }
}

@Module
public abstract class NetworkModule {
    @Binds
    public abstract INetworkLayer provideNetworkLayer(ProductionNetworkLayer productionNetworkLayer);
    // same as `public INetworkLayer prov(ProductionNetworkLayer prod) { return prod; }`

    @Provides
    @Singleton
    public OkHttpClient provideOkHttpClient() {
        return new /*create okHttpClient*/;
    }
}
EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
  • "Automatic dependency graph resolution" Does this mean that Dagger can look at all the method signatures with @Provides annotations and wire up the correct ones so that the return types of some methods feed the arguments of other methods? – neonDion Nov 23 '16 at 14:04
  • 1
    Yup! That's exactly it, like the "okhttpclient" example above. But it works across modules as long as they belong to the same component, and it works for "concrete types with `@Inject` annotated constructor that are either unscoped or in the same scope as the component" – EpicPandaForce Nov 23 '16 at 14:40
  • Now that I think about it, what you actually want is to have a flavor of in this scenario is the `SingletonComponent` class, and binding different modules into it. That is how you'd get your initial mock/prod `Injector` setup. – EpicPandaForce Nov 23 '16 at 15:46
  • 1
    So there'd be one SingletonComponent (SC) per flavor and each SC would specify which modules it wanted to use. So the prod SC would specify the modules containing prod dependencies and the mock SC would specify the modules containing the mock dependencies. And then since they're both just called SC and the modules use the same names, then in the rest of your code you could just call DaggerSingletonComponent.build().netModule(new NetModule()) and whether you got real objects or mock objects would depend on which flavor the project was set to? Does that sound right? – neonDion Nov 23 '16 at 17:03