dependency injection

Download Dependency Injection

If you can't read please download the document

Upload: indrit-selimi

Post on 16-Apr-2017

1.272 views

Category:

Technology


0 download

TRANSCRIPT

Dependency Injection

Practice, Practice, Practice

A young concert violinist got lost on his way to a performance. He stoped an old man on the corner and asked him how to get to Carnige Hall. The old man looked at the violinist and the violin tucked under his arm, and said: Practice, son, Practice!

Theory-Practice: a positive feedback system

Why DI?

public class Emailer {

private SpellChecker spellChecker;

public Emailer() {

this.spellChecker = new SpellChecker();

}

public void send(String text) { .. }

}

How about testing?

public class MockSpellChecker extends SpellChecker {

private boolean didCheckSpelling = false;

public boolean checkSpelling(String text) {

didCheckSpelling = true;

return true;

}

public boolean verifyDidCheckSpelling(){

return didCheckSpelling;

}

}

And what about the Open-Closed Principle?

The Factory Pattern

ClientFactoryServicepublic class EmailerFactory {

/* The Factory is explicit about the object it produces...*/

public Emailer newItalianEmailer() {

return new Emailer(new ItalianSpellChecker());

}

}

Emailer service = new EmailerFactory().newItalianEmailer();

@Test

public void testEmailer() {

MockSpellChecker spellChecker = ...

Emailer emailer = new Emailer(spellChecker);

emailer.send("Fatte DI in ufficio!");

assert ...;

}

What about the client?

public class EmailerClient {private Emailer emailer = new EmailerFactory().newItalianEmailer();public void sendEmail() {emailer.send(createMessage());}}

public class EmailerFactory {private static Emailer instance; // did you see the problem here?public Emailer newEmailer() {if (null == instance)return new Emailer(..);return instance;}static void set(Emailer mock) {instance = mock;}}

@Testpublic void testEmailClient() {MockEmailer mock = new MockEmailer();EmailerFactory.set(mock);new EmailClient().sendEmail();assert mock.correctlySent();}

@Testpublic void testEmailClient() {MockEmailer mock = new MockEmailer();EmailerFactory.set(mock);try {new EmailClient().sendEmail();assert mock.correctlySent();} finally {EmailerFactory.set(null);}}

The Service Locator Pattern

Is a generalization of the Factory

Emailer emailer = (Emailer) new ServiceLocator().get("ItalianEmailer");

Emailer emailer = (Emailer) new ServiceLocator().get("JapaneseEmailerWithPhoneAndEmail");

(as a Factory) suffers from same problems: testability and shared state

Do you remember the ServiceManager anti-pattern?

DI

Hollywood Principle

Behaviorally focused

Modular

Separation of concerns, Demeter's law, Decoupling

TESTABLE

My main(String... args) is better than yours...

public class EmailerClient {

private final Emailer emailer;

public EmailerClient(Emailer emailer){

this.emailer = emailer;

}

public void sendEmail() {

emailer.send(createMessage());

}

...

}

public static void main(String... args) throws Exception { // by hand

(new ApplicationFactory(args).create()).start();

}

public static void main(String... args) throws Exception { // using Guice

Injector injector = Guice.createInjector(new ApplicationModule(args));

(injector.getInstance(Application.class)).start();

}

What have changed?

Client code (EmailerClient) doesn't invoke the injector (separation of concerns)

The root-object is explicit (Application.class)

The injector obtains instances within current execution context (scoping)

How to identify a dependency

Dependency:

Contract

Variants

public interface SpellChecker {

boolean check(String text);

}

class ItalianSpellChecker implements SpellChecker {

public boolean check(String text) {/* check italian */ ...}

}

class EnglishSpellChecker implements SpellChecker {

public boolean check(String text) {/* check english */ ...}

}

(new Emailer(new EnglishSpellChecker())).send(Hello);

(new Emailer(new ItalianSpellChecker())).send(Ciao);

KEY:

Unique

Arbitrary

Explicit

String Keys (Spring)

Pros

Unique

Arbitrary

Explicit

Cons

Error-prone

No compile-time safe

You can't use Vim but IntelliJ Idea

Must be carefully choosen

Examples:

Identifying by Type

Pros

Compile-time safe

Right direction

Cons

Not unique

Not arbitrary

Explicitness?

Inflexible

Examples:

SpellChecker.class identifies EnglishSpellChecker or ItalianSpellChecker

Emailer.class identifies itself

Combinatorial Keys (Guice)

Key = [contract, variant]

Key1: [Emailer.class, english]

Key2: [Emailer.class, italian]

Key3: [Emailer.class, English]

Or better:

Key1: [Emailer.class, English.class]

Key2: [Emailer.class, Italian.class]

@Retention( RUNTIME )

@Target( { PARAMETER } )

public @interface English {

...

}

public class Emailer{private final SpellChecker spellChecker;@Injectpublic SpellCheckerClient(@English SpellChecker spellChecker) {this.spellChecker = spellChecker;}}

public class SpellingModule extends AbstractModule {@Overrideprotected void configure() { bind(SpellChecker.class).annotatedWith(English.class).to(EnglishSpellChecker.class);}

Injection Idioms

Setter injection

public class Emailer {

private SpellChecker spellChecker;

public void setSpellChecker(SpellChecker spellChecker) {

this. spellChecker = spellChecker;

}

}

Interface Injection (Deprecated)

Method Decoration (via AOP)

Constructor Injection

public class Emailer {

private final SpellChecker spellChecker; // potentially immutable

public Emailer(SpellChecker spellChecker){

this.spellChecker = spellChecker; // freezing the object graph

}

}

Constructor vs Setter
Injection

Constructor

Clear contract

Immutability support or temporal encapsulation

Ready for use/ Object validity

No need for noisy setters

Setter

Esplicitness (type indipendent)

Doesn't suffers from the pyramid/telescoping problem

Doesn't suffers from the circular dependency and some in-construction problems

GOF's Creational Patterns

Abstract Factory

Factory Method

Builder

Prototype

Singleton

The Reinjection Problem

When a long-lived dependent needs a short-lived dependencies

public class Granny {

private Apple apple;

public Granny(Apple apple) {

this.apple = apple;

}

public void eat() {

apple.consume();

apple.consume();

}

}

/**Provides specific object and scoped intances of those objects.*/public interface Provider {T get();}

public class AppleProvider implements Provider{public Apple get(){return new Apple();}

}

public class Granny {private Provider appleProvider;

public Granny(Provider ap) {this.appleProvider = ap;}

public void eat() {appleProvider.get().consume();appleProvider.get().consume();}}

Or using the builder pattern..

public class Granny {private AppleBuilder builder;

public Granny(AppleBuilder b) {this.builder = b;}

public void eat() {builder.buildRedApple().consume();builder.buildGreenApple().consume();}}

public class AppleBuilder {public Apple buildRedApple() {return new RedApple();}

public Apple buildGreenApple() {return new GreenApple();}}

and its Jochua's version...

The Contextual Injection Problem

public class NewsletterManager {

private final List recipients;

private final AssistedProvider deliverer;

public NewsletterManager(List rs,AssistedProvider dp){

this.recipients = rs;

this.deliverer = dp;

}

public void send(Newsletter letter) {

for (Recipient recipient : recipients) {

Deliverer d = deliverer.get(letter);

d.deliverTo(recipient);

}

}

}

public interface AssistedProvider {

T get(C context);

}

public class DelivererProvider implements AssistedProvider {

public Deliverer get(Newsletter letter) {

return new Deliverer(letter);

}

}

Scope

Is a fixed duration of time or method calls in which an object exists

A context under which a given key refers to the same instance

Mostly used: Singleton and No-Scope

Interesting scopes: Transaction, Web, Cache, Thread, Grid, Custom...

Why Scope?

Applies the Hollywood Principle to the state of the objects

The injector manages the latent state of your objects

The injector ensures that the services get new instance of dependencies as needed

Implicity separates state by context

Reduces the necessity for state-aware application logic (separation of concerns infrastructure/business logic)

Defining a Custom Scope

Guice

public interface Scope {

Provider scope(Key key, Provider unscoped);

}

public class TransactionScope implements Scope {

private final ThreadLocal, Object>>();

public Provider scope(

final Key key, final Provider unscoped) {

return new Provider() {

public T get() {

Map