Click here to load reader

Demystifying dependency Injection: Dagger and Toothpick

  • View
    760

  • Download
    4

Embed Size (px)

Text of Demystifying dependency Injection: Dagger and Toothpick

I come with knifes

I come with knifesA story of Daggers and Toothpicks

Droidcon Krakow 2016Danny [email protected]

Once upon a time

@PreusslerBerlin

A code base, one activity, needed a trackerNeeded in in a testable way2

A single activity

@PreusslerBerlin

A code base, one activity, needed a trackerNeeded in in a testable way3

public class LonelyActivity extends Activity {

@Override protected void onCreate(Bundle state) { super.onCreate(state); setContentView(R.layout.activity_main); }}

@PreusslerBerlin

4

needs to

@PreusslerBerlin

A code base, one activity, needed a trackerNeeded in in a testable way5

needs to support

@PreusslerBerlin

A code base, one activity, needed a trackerNeeded in in a testable way6

needs to supportTracking

@PreusslerBerlin

A code base, one activity, needed a trackerNeeded in in a testable way7

The journey to testability starts

@PreusslerBerlin

A code base, one activity, needed a trackerNeeded in in a testable way8

Starring

@PreusslerBerlin

9

Tracker as componentpublic interface Tracker {

void trackStarted();}

@PreusslerBerlin

10

GoogleAnalyticsTracker as Trackerpublic class GoogleAnalyticsTracker implements Tracker {

@Override public void trackStarted() { }}

@PreusslerBerlin

11

PrologThe Untestable

@PreusslerBerlin

12

public class LonelyActivity extends Activity {

private final Tracker tracker = new GoogleAnalyticsTracker();

@Override protected void onCreate(Bundle state) { super.onCreate(state); setContentView(R.layout.activity_main); tracker.trackStarted(); }}

@PreusslerBerlin

13

public class LonelyActivity extends Activity {

private final Tracker tracker = new GoogleAnalyticsTracker();

@Override protected void onCreate(Bundle state) { super.onCreate(state); setContentView(R.layout.activity_main); tracker.trackStarted(); }}

@PreusslerBerlin

14

public class LonelyActivity extends Activity {

private final Tracker tracker = new GoogleAnalyticsTracker();

@Override protected void onCreate(Bundle state) { super.onCreate(state); setContentView(R.layout.activity_main); tracker.trackStarted(); }}

Not testable!

@PreusslerBerlin

15

A voice out of the dark:Inversion of Control!

@PreusslerBerlin

16

A voice out of the dark:Inversion of Control!

@PreusslerBerlin

17

public class LonelyActivity extends Activity {

private final Tracker tracker = new GoogleAnalyticsTracker();

@Override protected void onCreate(Bundle state) { super.onCreate(state); setContentView(R.layout.activity_main); tracker.trackStarted(); }}

@PreusslerBerlin

18

Act IThe Factory

@PreusslerBerlin

19

public class Dependencies {

static Dependencies instance = new Dependencies();

public static Dependencies getInstance() { return instance; }

public Tracker getTracker() { return new GoogleAnalyticsTracker(); }

@PreusslerBerlin

20

public class Dependencies {

static Dependencies instance = new Dependencies();

public static Dependencies getInstance() { return instance; }

public Tracker getTracker() { return new GoogleAnalyticsTracker(); }

@PreusslerBerlin

21

public class Dependencies {

static Dependencies instance = new Dependencies();

public static Dependencies getInstance() { return instance; }

public Tracker getTracker() { return new GoogleAnalyticsTracker(); }

@PreusslerBerlin

22

import static Dependencies.getInstance;

class LonelyActivity extends Activity {

Tracker tracker = getInstance().getTracker();

@Override protected void onCreate(Bundle state) { super.onCreate(state);... tracker.trackStarted(); }}

@PreusslerBerlin

23

import static Dependencies.getInstance;

class LonelyActivity extends Activity {

Tracker tracker = getInstance().getTracker();

@Override protected void onCreate(Bundle state) { super.onCreate(state);... tracker.trackStarted(); }}

@PreusslerBerlin

24

@Mock Tracker tracker;

@Beforepublic void setup() { Dependencies.instance = mock(Dependencies.class); when(Dependencies.instance.getTracker()).thenReturn(tracker);}

@Testpublic void should_track() { new SimpleActivity().onCreate(null); verify(tracker).trackStarted();}

@PreusslerBerlin

25

@Mock Tracker tracker;

@Beforepublic void setup() { Dependencies.instance = mock(Dependencies.class); when(Dependencies.instance.getTracker()).thenReturn(tracker);}

@Testpublic void should_track() { new SimpleActivity().onCreate(null); verify(tracker).trackStarted();}

@PreusslerBerlin

26

@Mock Tracker tracker;

@Beforepublic void setup() { Dependencies.instance = mock(Dependencies.class); when(Dependencies.instance.getTracker()).thenReturn(tracker);}

@Testpublic void should_track() { new SimpleActivity().onCreate(null); verify(tracker).trackStarted();}

@PreusslerBerlin

27

+/- Somehow testable- Not very dynamic- Lots of code

@PreusslerBerlin

28

Act IIThe Map

@PreusslerBerlin

29

public class Dependencies {

static Map modules = new HashMap(); static { modules.put(Tracker.class, new GoogleAnalyticsTracker()); }

public static T get(Class clzz) { return (T) modules.get(clzz); }}

@PreusslerBerlin

30

public class Dependencies {

static Map modules = new HashMap(); static { modules.put(Tracker.class, new GoogleAnalyticsTracker()); }

public static T get(Class clzz) { return (T) modules.get(clzz); }}

@PreusslerBerlin

31

import static Dependencies.getInstance;

class LonelyActivity extends Activity {

Tracker tracker = getInstance().get(Tracker.class);

@Override protected void onCreate(Bundle state) { super.onCreate(state);... tracker.trackStarted(); }}

@PreusslerBerlin

Same as before32

import static Dependencies.getInstance;

class LonelyActivity extends Activity {

Tracker tracker = getInstance().get(Tracker.class);

@Override protected void onCreate(Bundle state) { super.onCreate(state);... tracker.trackStarted(); }}

@PreusslerBerlin

Same as before33

+ Testable+ Dynamic- Instances created at startup

@PreusslerBerlin

34

Act IIIReflection(s)

@PreusslerBerlin

35

public class Dependencies {

private final static Map modules = new HashMap();

static { modules.put(Tracker.class, GoogleAnalyticsTracker.class); }static T get(Class clzz) {

@PreusslerBerlin

Todo splitShow inject interfaceTODO show singleton! But needs lazy loading then!TODO in code!36

public class GoogleAnalyticsTracker implements Tracker {

GoogleAnalyticsTracker(Activity activity) {

...}

GoogleAnalyticsTracker(Application application) { ...}

GoogleAnalyticsTracker(Context context) { ...}...

?

@PreusslerBerlin

37

public class GoogleAnalyticsTracker implements Tracker {

GoogleAnalyticsTracker(Activity activity) {

...}

GoogleAnalyticsTracker(Application application) { ...}

@Inject GoogleAnalyticsTracker(Context context) { ...}...

@PreusslerBerlin

38

package javax.inject;

@Target({ METHOD, CONSTRUCTOR, FIELD })@Retention(RUNTIME)public @interface Inject {}

@PreusslerBerlin

Known as @Inject39

public class Dependencies {

private final static Map modules = new HashMap();

static { modules.put(Tracker.class, GoogleAnalyticsTracker.class); }static T get(Class clzz) {

@PreusslerBerlin

Todo splitShow inject interfaceTODO show singleton! But needs lazy loading then!TODO in code!40

static T get(Class clzz) {

Constructor[] constructors = clzz.getDeclaredConstructors();

for(Constructor ctr: constructors) {

@PreusslerBerlin

Todo splitShow inject interfaceTODO show singleton! But needs lazy loading then!TODO in code!41

static T get(Class clzz) {

Constructor[] constructors = clzz.getDeclaredConstructors();

for(Constructor ctr: constructors) {

@PreusslerBerlin

Todo splitShow inject interfaceTODO show singleton! But needs lazy loading then!TODO in code!42

...if(ctr.getDeclaredAnnotation(Inject.class) != null) { Class[] types = ctr.getParameterTypes(); Object[] params = new Object[types.length]; for (int i=1; i< params.length; i++) { params[i] = get(types[i]); }

@PreusslerBerlin

Todo splitShow inject interfaceTODO show singleton! But needs lazy loading then!TODO in code!43

if(ctr.getDeclaredAnnotation(Inject.class) != null) { Class[] type

Search related