A Problem - Solution Approach
Dependency Injection Framework
It means giving an object its instance variables.
Really. That’s it.
public class Example {
private Something something;
public Example() {
this.something = new Something();
}
public void DoStuff() {
...
something.doSomething();
...
}
}
public class Example {
private Something something;
public Example(Something someOtherThing) {
this.something = someOtherThing;
}
public void setSomething(Something someOtherOtherThing) {
this.something = someOtherOtherThing;
}
}
It’s handy for isolating classes during testing.
public class ExampleTest {
TestDoStuff() {
MockSomething mockSomething = new MockSomething();
// MockSomething is a subclass of Something, so we can
// "inject" it here:
Example example = new Example(mockSomething);
example.DoStuff();
mockSomething.AssertDoSomethingSuccessfully();
}
}
That’s it.
Dependency injection is really just passing in an instance variable.
Dagger is a dependency injection framework
for Java and Android.
Let's examine this
Dependency Inject without Dagger
new Person(new Body(new Blood()));
// OBlood, ABlood, BBlood, ABBlood
Separates
the creation of a client’s dependencies
from
the client’s behavior
all calls of new operator, newInstance() and others shouldn’t be invoked in places
other than Dagger’s Modules.
new Person(new Body(new Blood()));
// OBlood, ABlood, BBlood, ABBlood
public static void main(String[] args) {
OBloodPersonCreator OBloodPersonCreator
= DaggerOBloodPersonCreator.create();
Person OBloodPerson = OBloodPersonCreator.createPerson();
System.out.println(OBloodPerson);
ABloodPersonCreator ABloodPersonCreator
= DaggerABloodPersonCreator.create();
Person ABloodPerson = ABloodPersonCreator.createPerson();
System.out.println(ABloodPerson);
}
all calls of new operator, newInstance() and others
shouldn’t be invoked in places
other than Dagger’s Modules.
injects the Body class into another class when needed
public class Body {
@Inject
public Body(){}
}
public class Person{
Body body;
@Inject
public Person(Body body){
this.body = body;
}
}
public class Body {
@Inject
Blood blood;
@Inject
public Body(){}
public Blood getBlood() {
return blood;
}
public void setBlood(Blood blood) {
this.blood = blood;
}
}
injects concrete classes for an interface
@Module
public class RandomBloodModule {
private static Blood blood;
public RandomBloodModule() {
}
@Provides
static Blood provideBlood(){
if(blood != null) {
return blood;
}
Random rnd = new Random();
int rndNumber = rnd.nextInt(3);
switch (rndNumber){
case 0: blood = new A_Blood(); break;
case 1: blood = new B_Blood(); break;
case 2: blood = new AB_Blood(); break;
case 4: blood = new O_Blood(); break;
default: blood = new A_Blood();
}
return blood;
}
}
an interface that specifies which modules to use
@Component( modules = RandomBloodModule.class )
public interface PersonCreator {
Person createPerson();
}
public static void main(String[] args) {
PersonCreator PersonCreator = DaggerPersonCreator.create();
Person person = PersonCreator.createPerson();
System.out.println(Operson);
}
always inject the same instance
@Module
public class ABloodPersonCreatingModule {
@Singleton
@Provides
Blood provideBlood(OBlood oBlood) {
return oBlood;
}
}
@Singleton
public class OBlood implements Blood {
@Override
public String getKindOfBlood() {
return "O";
}
}
2 provide methods that have the same return type
@Named annotation differentiates two instances.
@Module
public class BloodModule {
@Provides
@Named("oblood")
Blood provideOBlood() {
return new OBlood();
}
@Provides
@Named("ablood")
Blood provideABlood() {
return new ABlood();
}
}
public class Body {
@Inject
@Named("oblood")
Blood oBlood;
@Inject
@Named("ablood")
Blood aBlood;
}
Same use as @Named
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface OBloodQualifier {
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface ABloodQualifier {
}
public class Body {
@Inject
@OBloodQualifier
Blood oBlood;
@Inject
@ABloodQualifier
Blood aBlood;
}
package dependencies from different modules into a SET
@Module
public class BloodModule {
@Provides
@IntoSet
Blood provideOBlood() {
return new OBlood();
}
@Provides
@IntoSet
Blood provideABlood() {
return new ABlood();
}
}
@Component(modules = {
BloodModule.class
})
public interface BloodPersonCreator {
Set<Blood> getBloods();
}
It could then be injected all together to the target object.
@Module
public class BloodModule {
@Provides
@IntoMap
@StringKey("oblood")
Blood provideOBlood() {
return new OBlood();
}
@Provides
@IntoMap
@StringKey("ablood")
Blood provideABlood() {
return new ABlood();
}
}
@Singleton
@Component(modules = {
BloodModule.class
})
public interface BloodPersonCreator {
Map<String, Blood> getBloods();
}
package dependencies from different modules into a MAP
It could then be injected all together to the target object.
@MapKey
@interface BloodEnumKey {
BloodEnum value();
}
@Module
class BloodModule {
@Provides
@IntoMap
@BloodEnumKey(Blood.O)
Blood provideOBlood() {
return new OBlood();
}
}
class Body {
@Inject
Body (Map<SomeEnum, Integer> map) {
assert map.get(BloodEnum.O)
.equals(new OBlood());
}
}
Same use as @IntoMap, but we define our own Key
Instead of using custom MapKey, you can use default keys provided by dagger — @StringKey, @IntKey, @LongKey, @ClassKey
Generate the static create() method for
multi-parameters constructor
@AutoFactory
public class Soul {
String type;
public Soul(@Provided Body body,
String type) {
this.type = type;
}
}
public class Person {
Body body;
Soul soul;
@Inject
public Person(Body body, SoulFactory soulFactory) {
this.body = body;
soul = soulFactory.create("male");
}
}