di & dagger

Post on 16-Mar-2018

90 Views

Category:

Software

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

(With Dagger2)

Dependency Injection

Hi There!

● Software Developer for three years○ Medical Research

○ eCommerce

● Android Developer at heart and profession

● Software Developer at Gett

Game Plan*

●What’s in it for me?

●Dependency Injection Principles

●Dagger2

*To the best of our ability :)

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?”

Testable And Maintainable Code?

public class SomeClass {

private SomeOtherClass someOtherClass;

public SomeClass() {

someOtherClass = new SomeOtherClass;

}

}

Testable And Maintainable Code!

public class Customer {

private Breakfast breakfast;

public Customer(Breakfast breakfast) {

this.breakfast = breakfast;

}

}

Game Plan

●What’s in it for me?

●Dependency Injection Principles

●Dagger2

Breakfast!

Breakfast!

Breakfast

Coffee OmeletteBreadJuice Salad

Water SugarMilk VeggiesSaltEggs Olive OilFruit

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

So how do we use dependencies?

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

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

Ready for some extremely difficult code?

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;

}

Okay great, can we go now?

Just one questionHow should we create our Breakfast object?

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

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

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

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

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

Examples

Game Plan*

●Purpose

●Dependency Injection Principles

●Dagger2

*To the best of our ability :)

Why Dagger2?

●Designed for low-end devices

●Generates new code in your application

○ No Reflection

How Do Restaurants Work?

How Does Dagger2 Work?

Modules Component Clients

Module - The Supplier

●Goal

○ Provides dependencies

○ Providing your dependencies context

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

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();

}

}

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

Components - The Restaurants

●Goal

○ Bridges between the suppliers and the customers

○ Handles the final touches of the “basic materials”

Components At a High Level

●Gathers all of the ingredients from all its suppliers

●Serves a defined set of customers

RestaurantComponent example

@Component(modules = {BreakfastSupplierModule.class})

public interface RestaurantComponent {

// injection methods

void inject(Customer aCustomer);

}

Client Classes

●Use the @Inject annotation to get what they need

●Are supplied through the component

@Inject example

public class Customer {

@Inject Omelette omelette;

@Inject Coffee coffee;

// More code goes here...

}

And Now we “Build”

Component Usage

●The customer depends on the component

●The customer asks the component to serve itself

Component Usage - One way

@Inject Omellete omelette;

@Inject Coffee coffee;

public Customer()

DaggerRestaurantComponent.builder()

.build().breakfastSupplierModule(new BreakfastSupplierModule)

.inject(this);

}

Component Usage - Better way

@Inject Omellete omelette;

@Inject Coffee coffee;

public Customer(BreakfastSupplierModule breakfastModule) {

DaggerRestaurantComponent.builder()

.build().breakfastSupplierModule(breakfastModule)

.inject(this);

}

Component Usage - Dagger Master Way

@Inject Omellete omelette;

@Inject Coffee coffee;

public Customer(RestaurantComponent restaurantComponent) {

restaurantComponent.inject(this);

}

Best Practices

Dos and Don’ts

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

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

Law of DemeterDon’t talk to strangers

Don’t!

public class MenuManager {

private SharedPreferences sharedPreferences;

public MenuManager(Context context) {

this.sharedPreferences = context.getSharedPreferences(PREFERENCES_NAME,

Context.MODE_PRIVATE);

}

}

Do!

public class MenuManager {

private SharedPreferences sharedPreferences;

public MenuManager(SharedPreferences sharedPreferences) {

this.sharedPreferences = sharedPreferences;

}

}

Verdict

●Testing is difficult

●Context can do too much for its own good

● I can just pass the SharedPreferences, right?

The client should just reflect its API through its dependencies

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

Don’t!

public class Customer {

// Another Way simply initialize

public Customer() {

this.breakfast = new Breakfast();

}

}

Don’t!

public class Customer {

// One Way - Inject through component

@Inject Breakfast breakfast;

}

Do!

public class Customer {

private Breakfast breakfast;

public SomeClient(Breakfast breakfast) {

mSomeService = someService;

}

}

Verdict

●Difficult to test

●Client does not reflect its API

●What if Breakfast has dependencies?

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

Constructor

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

Don’t!

public class MenuView extends LinearLayout {

// Another Way

public Breakfast() {

breakfast = new Breakfast()

}

}

Don’t!

public class MenuView extends LinearLayout {

// One Way

@Inject Breakfast breakfast;

}

Kind of works, but not really...

public class MenuView extends LinearLayout {

public MenuView(Context context, Breakfast breakfast) {

this.breakfast = breakfast;

}

}

Do!

public class MenuView extends LinearLayout {

private Breakfast breakfast;

public void setBreakfast(Breakfast breakfast) {

this.breakfast = breakfast;

}

}

Verdict

●Difficult to test

●View does not reflect its API

●What if Breakfast has dependencies?

●Usage may be limited

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

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

Don’t!

public class BreakfastActivity extends AppCompatActivity {

private Breakfast breakfast;

public void onCreate(Bundle savedInstanceState) {

this.breakfast = new Breakfast(savedInstanceState);

}

}

Do!

public class BreakfastActivity extends AppCompatActivity {

@Inject Breakfast breakfast;

public SomeClient() {

getActivityComponent().inject(this);

}

}

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)

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

use dagger’s @Inject feature

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

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();

}

}

BreakfastSupplierModule - Broken Up

@Module

public class CoffeeModule {

@Provides

Omelette provideCoffee() {

return new BlackCoffee();

}

}

RestaurantComponent Result

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

public interface RestaurantComponent {

// injection methods

void inject(Customer aCustomer);

}

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

Thank yous

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

Thank you

top related