PROBLEMS WHEN UNIT TESTING You need to intercept the Factory call mock myObject in order to write unit tests. public class SomeClass { private final Object myObject;
public SomeClass() { myObject = Factory.getObject(); // Hard to replace this } }
Basically, instead of having your objects creating a dependency or asking a factory object to make one for them, you pass the needed dependencies in to the object externally. .. or you can use a dependency injector that builds the dependency graph for you. INJECTING DEPENDENCIES
PERFORMANCE COMPARISON OF DI LIBRARIES Mar 7, 2016 data, by NimbleDroid [1] Average duration of 5455 injections: Roboguice 3923 ms Dagger 1 154 ms Dagger 2 62 ms
The best classes in any application are the ones that do stuff: the BarcodeDecoder, the KoopaPhysicsEngine, and the AudioStreamer. These classes have dependencies; perhaps a BarcodeCameraFinder, DefaultPhysicsEngine, and an HttpStreamer. DAGGER 2
To contrast, the worst classes in any application are the ones that take up space without doing much at all: the BarcodeDecoderFactory, the CameraServiceLoader, and the MutableContextWrapper. These classes are the clumsy duct tape that wires the interesting stuff together.
Dagger is a replacement for these Factory classes that implements the dependency injection design pattern without the burden of writing the boilerplate.
By building on standard javax.inject annotations, each class is easy to test. You don’t need a bunch of boilerplate just to swap the RpcCreditCardService out for a FakeCreditCardService.
Dependency injection isn’t just for testing. It also makes it easy to create reusable, interchangeable modules. You can share the same AuthenticationModule across all of your apps. You can run DevLoggingModule during development and ProdLoggingModule in production to get the right behavior in each situation.
When a new instance is requested, Dagger will obtain the required parameters values and invoke this constructor. class Thermosiphon implements Pump { private final Heater heater;
Dagger can inject fields directly. Add a no-argument constructor with the @Inject annotation to indicate that Dagger may create instances as well. class CoffeeMaker { @Inject Heater heater; @Inject Pump pump;
ApiConfiguration apiConfiguration = IoUtil.initApiConfiguration(); singletonComponent = DaggerSingletonComponent.builder() .singletonModule(new SingletonModule(apiConfiguration)) .apiModule(new ApiModule()) .build(); Dagger will create modules with default constructors for you, so you don’t need to create them.
DAGGER API • @Module + @Provides: mechanism for providing dependencies • @Inject: mechanism for requesting dependencies • @Component: bridge between modules and injections
SCOPES • @Singleton is the largest scope • Custom annotations for semantic clarity • Dagger will inject the same instance inside the scope (local singletons) • If we don’t use scopes then Dagger will create new dependencies every time we inject
Annotate an @Provides method or injectable class with @Singleton. The graph will use a single instance of the value for all of its clients. @Module public class ApiModule {
Annotate an @Provides method or injectable class with @Singleton. The graph will use a single instance of the value for all of its clients. @Module public class ApiModule {
COMPONENT SCOPES The components themselves need to declare which scope they intend to represent. @Component(modules = DripCoffeeModule.class) @Singleton interface CoffeeShop { CoffeeMaker maker(); }
QUALIFIERS Sometimes the type alone is insufficient to identify a dependency. @Provides @Named("hot plate") Heater provideHotPlateHeater() { return new ElectricHeater(70); }
@Provides @Named("water") Heater provideWaterHeater() { return new ElectricHeater(93); } class ExpensiveCoffeeMaker { @Inject @Named("water") Heater waterHeater;
COMPILE-TIME VALIDATION The Dagger annotation processor is strict and will cause a compiler error if any bindings are invalid or incomplete. @Module class DripCoffeeModule { @Provides Heater provideHeater(Executor executor) { return new CpuHeater(executor); } } [ERROR] error: java.util.concurrent.Executor cannot be provided without an @Provides-annotated method.
COMPILE-TIME CODE GENERATION CoffeeMaker_Factory.java and CoffeeMaker_MembersInjector.java? You shouldn’t need to use them directly. The only generated types you should refer to in your code are the ones prefixed with Dagger for your component.