74
(With Dagger2) Dependency Injection

Di & dagger

Embed Size (px)

Citation preview

Page 1: Di & dagger

(With Dagger2)

Dependency Injection

Page 2: Di & dagger
Page 3: Di & dagger

Hi There!

● Software Developer for three years○ Medical Research

○ eCommerce

● Android Developer at heart and profession

● Software Developer at Gett

Page 4: Di & dagger

Game Plan*

●What’s in it for me?

●Dependency Injection Principles

●Dagger2

*To the best of our ability :)

Page 5: Di & dagger

What’s in it for me?

●Testable Code

○ “If I were to plug this code somewhere else, is it guaranteed to work the same

way?”

●Maintainable Code

○ “How many ‘what the..’ moments am I having while reading this code?”

Page 6: Di & dagger

Testable And Maintainable Code?

public class SomeClass {

private SomeOtherClass someOtherClass;

public SomeClass() {

someOtherClass = new SomeOtherClass;

}

}

Page 7: Di & dagger

Testable And Maintainable Code!

public class Customer {

private Breakfast breakfast;

public Customer(Breakfast breakfast) {

this.breakfast = breakfast;

}

}

Page 8: Di & dagger

Game Plan

●What’s in it for me?

●Dependency Injection Principles

●Dagger2

Page 9: Di & dagger

Breakfast!

Page 10: Di & dagger

Breakfast!

Breakfast

Coffee OmeletteBreadJuice Salad

Water SugarMilk VeggiesSaltEggs Olive OilFruit

Page 11: Di & dagger

What is a Dependency?

●A state in which an object uses a function of another object

○ Class A is dependent on class B if and only if A needs B in order to function

●Defined by “import” statements

Page 12: Di & dagger

So how do we use dependencies?

Page 13: Di & dagger

So how do we use dependencies?

● new statements

○ this.breakfast = new Breakfast();

●Singleton objects

○ this.breakfast = Breakfast.getInstance();

●Through constructors and methods

○ this.breakfast = breakfastParameter;

● Inversion Of Control

Page 14: Di & dagger

What is Dependency Injection?

●A technique whereby one object supplies the dependencies of

another object (wikipedia)

●A technique whereby one object supplies the dependencies of

another object (wikipedia)

●There are many ways to do it

○ We just saw four ways!

●DI acronym

Page 15: Di & dagger

Ready for some extremely difficult code?

Page 16: Di & dagger

Extremely difficult code ahead!

// Constructor

public Customer(Breakfast breakfast) {

// Save the reference to the dependency as passed in by the creator

this.mBreakfast = breakfast;

}

// Setter Method

public void setBreakfast(Breakfast breakfast) {

// Save the reference to the dependency as passed in by the creator

this.mBreakfast = breakfast;

}

Page 17: Di & dagger

Okay great, can we go now?

Page 18: Di & dagger

Just one questionHow should we create our Breakfast object?

Page 19: Di & dagger

How do we create a breakfast object?

// .. Some code above

Breakfast breakfast = new Breakfast(

new Coffee(

// Dependencies go here..

), new Juice(

// Dependencies go here..

// Some more initializations

);

// Give the client their breakfast

Page 20: Di & dagger

How do we create a breakfast object?

// .. Some code above

Breakfast breakfast = new Breakfast(

new BlackCoffee(Coffee(

// Dependencies go here..

), new Juice(

// Dependencies go here..

// Some more initializations

);

// Give the client their breakfast

Page 21: Di & dagger

How do we create a breakfast object?

// .. Some code above

Breakfast breakfast = new Breakfast(

new BlackCoffee(Coffee(

new Sugarless,

new Skimlesss(new Milk()

// Dependencies go here..

), new Juice(

// Dependencies go here..

// Some more initializations

);

// Give the client their breakfast

Page 22: Di & dagger

Some questions to consider

●What if Breakfast is a supertype of other breakfast types?

○ Factories could work

●What if Breakfast is a singleton in the system?

○ Sure, but singletons are difficult to test

●Can we share breakfast instances with different clients?

○ Kind of, but it’s difficult to maintain

Page 23: Di & dagger

Dependency Injection - A Technique

●A technique whereby one object supplies the dependencies of

another object (wikipedia)

● Just like breakfast, I could do it myself

●But sometimes I want a restaurant to do it for me

○ Because I’m lazy

○ Because they make it better

○ [Insert whatever reason you want here]

● Inversion of control

Page 24: Di & dagger

Examples

Page 25: Di & dagger

Game Plan*

●Purpose

●Dependency Injection Principles

●Dagger2

*To the best of our ability :)

Page 26: Di & dagger

Why Dagger2?

●Designed for low-end devices

●Generates new code in your application

○ No Reflection

Page 27: Di & dagger

How Do Restaurants Work?

Page 28: Di & dagger

How Does Dagger2 Work?

Modules Component Clients

Page 29: Di & dagger

Module - The Supplier

●Goal

○ Provides dependencies

○ Providing your dependencies context

Page 30: Di & dagger

Modules At A High Level

●A supplier supplies materials

● It declares what it supplies as a contract

○ The restaurant can only get what the supplier supplies

●May depend on material it can supply

●May depend on material it cannot supply

Page 31: Di & dagger

BreakfastSupplierModule example

@Module

public class BreakfastSupplierModule {

@Provides

Omelette provideOmelette(Eggs eggs) { // The eggs will be supplied from the method below

return new ScrambledOmelette();

}

@Provides

Coffee provideCoffee() { // Method name does not matter

return new BlackCoffee();

}

@Provides

Eggs provideEggs() {

return new Eggs();

}

}

Page 32: Di & dagger

Modules - Some FAQ

●Unless stated otherwise

○ The module recreates each object it provides

○ Read on @Singleton for single-instance

●May depend on other module’s dependencies

●May depend on its own dependencies

Page 33: Di & dagger

Components - The Restaurants

●Goal

○ Bridges between the suppliers and the customers

○ Handles the final touches of the “basic materials”

Page 34: Di & dagger

Components At a High Level

●Gathers all of the ingredients from all its suppliers

●Serves a defined set of customers

Page 35: Di & dagger

RestaurantComponent example

@Component(modules = {BreakfastSupplierModule.class})

public interface RestaurantComponent {

// injection methods

void inject(Customer aCustomer);

}

Page 36: Di & dagger

Client Classes

●Use the @Inject annotation to get what they need

●Are supplied through the component

Page 37: Di & dagger

@Inject example

public class Customer {

@Inject Omelette omelette;

@Inject Coffee coffee;

// More code goes here...

}

Page 38: Di & dagger

And Now we “Build”

Page 39: Di & dagger

Component Usage

●The customer depends on the component

●The customer asks the component to serve itself

Page 40: Di & dagger

Component Usage - One way

@Inject Omellete omelette;

@Inject Coffee coffee;

public Customer()

DaggerRestaurantComponent.builder()

.build().breakfastSupplierModule(new BreakfastSupplierModule)

.inject(this);

}

Page 41: Di & dagger

Component Usage - Better way

@Inject Omellete omelette;

@Inject Coffee coffee;

public Customer(BreakfastSupplierModule breakfastModule) {

DaggerRestaurantComponent.builder()

.build().breakfastSupplierModule(breakfastModule)

.inject(this);

}

Page 42: Di & dagger

Component Usage - Dagger Master Way

@Inject Omellete omelette;

@Inject Coffee coffee;

public Customer(RestaurantComponent restaurantComponent) {

restaurantComponent.inject(this);

}

Page 43: Di & dagger

Best Practices

Dos and Don’ts

Page 44: Di & dagger

Don’t @Inject constructorYou don’t know where you’ll find yourself

Seriously… Just don’t use @Inject constructor...

Page 45: Di & dagger

Law of DemeterDon’t talk to strangers

Page 46: Di & dagger

Don’t!

public class MenuManager {

private SharedPreferences sharedPreferences;

public MenuManager(Context context) {

this.sharedPreferences = context.getSharedPreferences(PREFERENCES_NAME,

Context.MODE_PRIVATE);

}

}

Page 47: Di & dagger

Do!

public class MenuManager {

private SharedPreferences sharedPreferences;

public MenuManager(SharedPreferences sharedPreferences) {

this.sharedPreferences = sharedPreferences;

}

}

Page 48: Di & dagger

Verdict

●Testing is difficult

●Context can do too much for its own good

● I can just pass the SharedPreferences, right?

Page 49: Di & dagger

The client should just reflect its API through its dependencies

Page 50: Di & dagger

Constructor InjectionMy dependencies are created before me, and my creation can still be controlled

Page 51: Di & dagger

Don’t!

public class Customer {

// Another Way simply initialize

public Customer() {

this.breakfast = new Breakfast();

}

}

Page 52: Di & dagger

Don’t!

public class Customer {

// One Way - Inject through component

@Inject Breakfast breakfast;

}

Page 53: Di & dagger

Do!

public class Customer {

private Breakfast breakfast;

public SomeClient(Breakfast breakfast) {

mSomeService = someService;

}

}

Page 54: Di & dagger

Verdict

●Difficult to test

●Client does not reflect its API

●What if Breakfast has dependencies?

Page 55: Di & dagger

If we can create the object on our own, we’ll just pass the dependencies through the

Constructor

Page 56: Di & dagger

Setter Method InjectionMy dependencies are created after me, but my creation can still be controlled

Page 57: Di & dagger

Don’t!

public class MenuView extends LinearLayout {

// Another Way

public Breakfast() {

breakfast = new Breakfast()

}

}

Page 58: Di & dagger

Don’t!

public class MenuView extends LinearLayout {

// One Way

@Inject Breakfast breakfast;

}

Page 59: Di & dagger

Kind of works, but not really...

public class MenuView extends LinearLayout {

public MenuView(Context context, Breakfast breakfast) {

this.breakfast = breakfast;

}

}

Page 60: Di & dagger

Do!

public class MenuView extends LinearLayout {

private Breakfast breakfast;

public void setBreakfast(Breakfast breakfast) {

this.breakfast = breakfast;

}

}

Page 61: Di & dagger

Verdict

●Difficult to test

●View does not reflect its API

●What if Breakfast has dependencies?

●Usage may be limited

Page 62: Di & dagger

If the object may be created by the system, and not us, but we have access to it through a pointer, we’ll use setter methods to inject

dependencies

Page 63: Di & dagger

@Inject for Framework ComponentsThe user can’t create me, and can only use me

Page 64: Di & dagger

Don’t!

public class BreakfastActivity extends AppCompatActivity {

private Breakfast breakfast;

public void onCreate(Bundle savedInstanceState) {

this.breakfast = new Breakfast(savedInstanceState);

}

}

Page 65: Di & dagger

Do!

public class BreakfastActivity extends AppCompatActivity {

@Inject Breakfast breakfast;

public SomeClient() {

getActivityComponent().inject(this);

}

}

Page 66: Di & dagger

Verdict

●System components are only interactable through callbacks

○ We cannot hold references to them, or create them

●Use @Inject to declare their needed dependencies

●Use statically created components to inject them

○ Testability comes through modules

○ Testability comes through extension (TestableObject <- Object)

Page 67: Di & dagger

If the object may only be created by the system, and we may not reference it, we’ll

use dagger’s @Inject feature

Page 68: Di & dagger

Other Tips

● Look at the generated code

○ It’s code that is added to your application

●Use separate @Modules for each feature

○ Modules are classes that can be constructed

○ Provide clarity and customization

Page 69: Di & dagger

BreakfastSupplierModule - Broken Up

@Module

public class OmeletteModule {

@Provides

Omelette provideOmelette(Eggs eggs) { // The eggs will be supplied from

the method below

return new ScrambledOmelette();

}

@Provides

Eggs provideEggs() {

return new Eggs();

}

}

Page 70: Di & dagger

BreakfastSupplierModule - Broken Up

@Module

public class CoffeeModule {

@Provides

Omelette provideCoffee() {

return new BlackCoffee();

}

}

Page 71: Di & dagger

RestaurantComponent Result

@Component(modules = {OmeletteModule.class, CoffeeModule.class})

public interface RestaurantComponent {

// injection methods

void inject(Customer aCustomer);

}

Page 72: Di & dagger

Other Tips

● Look at the generated code

○ It’s code that is added to your application

●Use separate @Modules for each feature● Read on Dagger2’s many (many) features

● Kotlin users beware

Page 73: Di & dagger

Thank yous

● https://www.techyourchance.com○ Inspiration, ideas and overall thank yous

Page 74: Di & dagger

Thank you