SlideShare une entreprise Scribd logo
1  sur  53
Télécharger pour lire hors ligne
Effective Dependency
       Injection
Nicholas Behrens and Jerome Dochez
            Google Inc.
This talk

1. Quick DI refresher
2. Guidelines for effective DI
3. Composing applications using DI
4. Testing code using DI
5. Applying DI to existing code
1. Quick DI Refresher
DI = Dependency Injection

Declare what you need, not how to get it

PaymentProcessor(
    CreditCardService creditCardService) {
  this.creditCardService = creditCardService;
  this.riskAssessor =
      new RiskAssessorImpl(...);
}
DI = Dependency Injection

+ Loose coupling, facilitate reuse and testing
- Complexity, runtime failures

Many frameworks with many tools

Important to establish good conventions
DI Framework

● Providers
● Bindings
● @Inject
● Module [= collection of Bindings]
● Injector [= collection of Modules]
  ○ injector.getInstance(type)
● Scopes
2. Guidelines for effective DI
Explicit > Implicit

● Ensure explicit bindings for all injected types
   ○ bind(RequestFilter.class);


● Implicit bindings dangerous
  ○ Removing an explicit binding
  ○ Pulling in undesired explicit binding
Inject exactly what you need

Bad:
PaymentProcessor(User user) {
    this.account = user.getAccount();
}


Better:
PaymentProcessor(Account account) {
    this.account = account;
}
Prefer constructor injection

● private final fields
● Immutability, thread-safety
● Objects valid post-construction
● Match developer expectations
Avoid work in constructors

● Separation of concerns, facilitates testing,
  "standard" constructor
● In Guice: runtime ProvisionException
  easily propagates up to top level
● Initialize after constructing object graph
If you must do work in a constructor

     interface DataStoreProvider<T> extends CheckedProvider<T> {

         T get() throws DataStoreException;

     }




Guice-specific
If you must do work in a constructor
     (cont'd)
     @CheckedProvides(DataStoreProvider.class)

     Contacts provideContacts(UserId userId, DataStore dataStore)

           throws DataStoreException {

         return dataStore.readUserContacts(userId);

     }



     @Override protected void configure() {

         install(ThrowingProviderBinder.forModule(this));

     }

Guice-specific
If you must do work in a constructor
     (cont'd)
     @Inject ContactsFormatter(

           DataStoreProvider<Contacts> contactsProvider) {

         this.contactsProvider = contactsProvider;

     }



     String formatContacts() throws DataStoreException {

         Contacts contacts = contactsProvider.get();   // throws

         ...

     }

Guice-specific
If you must do work in a constructor
     (cont'd)
     Problems
     ● More complex, verbose bindings
     ● More setup work for tests
     ● Business logic creep
     ● Viral


Guice-specific
Avoid direct deps on Injector

● No longer declaring specific deps
● Injector allows dep on anything
● Easy to start using Injector, hard to stop
Avoid binding very general types

Bad
bindConstant().to(8080);
@Inject int port;


Better
bindConstant().annotatedWith(ForHttp.class)
    .to(8080);
@Inject @ForHttp int port;
Avoid parameterized binding
annotations
At first it seems convenient...
@Named("http") int httpPort;


...but then you do
@Named("HTTP") int httpPort;


No help from the IDE, more runtime errors.
Harder to track down injection sites.
Using parameterized binding
annotations safely
Consistently use constants (enums!) for parameters:

@ForPort(Ports.HTTP) int httpPort;


@FeatureEnabled(Features.X) boolean xEnabled;
Keep modules fast and side-effect
     free

     Make it easy to compose modules

     Avoid
     ● bind().toInstance(...)
     ● requestStaticInjection(...)
     ● Serial eager initialization of objects



Guice-specific
Prefer Null Objects over @Nullable
@Provides AbuseService provideAbuseService(
    @ForAbuse String abuseServiceAddress) {
  if (abuseServiceAddress.isEmpty()) { return null; }
  return new AbuseServiceImpl(abuseServiceAddress);
}

class AbuseChecker {
  @Inject AbuseChecker(
    @Nullable AbuseService abuseService) { ... }

  void doAbuseCheck(...) {
    if (abuseService != null) { ... )
  }


Bind StubAbuseService instead of null. Simplify
AbuseChecker.
Use Provider to avoid unnecessary
provisioning
@Provides AbuseService provideAbuseService(
    @UseNewAbuseService boolean useNewAbuseService,
    Provider<OldAbuseServiceImpl> oldAbuseService,
    Provider<NewAbuseServiceImpl> newAbuseService) {

    return useNewAbuseService.get()
        ? newAbuseService.get()
        : oldAbuseService.get();
}
Scoping as an alternative to context
public interface PipelineVisitor() {
   void visit(TypeToVisit typeToVisit);
}

public class Pipeline() {
   private final PipelineVisitor[] visitors = {
     new Visitor1(), new Visitor2(), ...
   }
   public void visit(TypeToVisit typeToVisit) {
     ...
   }
}
Requirements creep in, you add
more parameters...
public interface PipelineVisitor() {
   void visit(TypeToVisit typeToVisit, User user);
}
public interface PipelineVisitor() {
   void visit(TypeToVisit typeToVisit,
        User user, Logger logger);
}
public interface PipelineVisitor() {
   void visit(TypeToVisit typeToVisit,
       User user, Logger logger, RequestParams params);
}



and so on...
Ah yes, the Context to the rescue
public interface PipelineVisitor() {
   void visit(PipelineContext context);
}
public Class PipelineContext {
   TypeToVisit typeToVisit;
   User   user;
   Logging logging;
   ... and many more ...
}
and adapts the Pipeline itself.
public class Pipeline() {
   private final PipelineVisitor[] visitors = { ... };
   OutputType visit(TypeToVisit in, User user, Logging
log)
   {
       OutputType out = new OutputType();
       PipelineContext context =
          new PipelineContext(in, user, log);
       for (PipelineVisitor visitor : visitors) {
          visitor.visit(context);
       }
   }
}

what about testing, isolation,...
First : remove the context
public interface Visitor {
   void visit(TypeToVisit in);
}

public class LoggerVisitor {
   private final Logger logger;
   @Inject public LoggerVisitor(Logger logger) {...}
   void visit(InputType in, Output out) {
       logger.log(Level.INFO, "Visiting " + in);
}
public class UserDependentVisitor {
   private final User user;
   ...
}
Second : use a Multi-Binder
class VisitorsModule extend AbstractModule {
   @Override protected void configure() {
       Multibinder<PipelineVisitor> visitorBinder =
          Multibinder.newSetBinder(
              binder(), PipelineVisitor.class);

       visitorBinder.addBinding().to(LoggerVisitor.class);
       visitorBinder.addBinding()
          .to(UserDependentVisitor.class);
   }


Now we can inject a Set<Visitor>
Inject into Pipeline
public class Pipeline() {
   private final Set<Provider<PipelineVisitor.class>>
visitors

   @Inject
   Pipeline(Set<Provider<PipelineVisitor.class>> visitors) {
      this.visitors = visitors;
   }

   public void visit(TypeToVisit in) {
       for (Provider<PipelineVisitor.class> visitor :
visitors)
       {
          visitor.get().visit(in);
   ...
Testing Visitors and Pipeline
public void testVisitor() {
   Visitor v = new FirstVisitor(...);
   v.visit(in);
   assert(...)
}

public void testPipeline() {
   Visitor v = Mockito.mock(Visitor.class);
   Pipeline p = new Pipeline(Sets.of(Providers.of(v)));
   TypeToVisit in = new TypeToVisit(...);
   p.visit(in);
   Mockito.verify(v).visit(eq(in),
       any(Output.class));
}
3. Composing applications using DI
Modular Java!
Use a Service-Based Architecture
● All services defined as an interface
● Implementations package-private
● Package-level Module binds

No need for OSGi unless you need:
● Runtime extensibility
● Runtime versioning
Modular Java: Declare API and Impl
API: (interfaces, enums, binding annotations)
public interface SpamChecker {
   void checkIsSpam(...);
}

Implementation: (package private)
class SpamCheckerImpl implements SpamChecker {
   void checkIsSpam(...) { ... }
}
Modular Java: Colocate bindings
In the same package as SpamChecker*:
public class SpamCheckerModule extends AbstractModule {
  @Override
  public void configure() {
    bind(SpamCheckerImpl.class).in(Singleton.class);
    bind(SpamChecker.class).to(SpamCheckerImpl.class);
  }
}



To use in your app, install SpamCheckerModule
and then inject SpamChecker as needed.
Modular Java: Document deps
Document what your module requires:

class SpamCheckerModule ... {
  @Override protected void configure() {
    requireBinding(Clock.class);
    requireBinding(CryptoService.class);
    ...
  }
}
Modular Java: Package it all up
1. Maven
  a. All public interfaces in -api.jar module
  b. All implementations and modules in -impl.jar
  c. api module should only be used for compiling
     dependent modules, impl module should be used for
     assembling the application

2. OSGi
  a. Export interfaces and Module only
  b. Avoid versioning at runtime
4. Testing code using DI
Unit testing a class using DI

public class SubjectUnderTest {

    @Inject
    SubjectUnderTest(Service1 s1, Service2 s2) {
      // Assign to fields
    }

    String doSomething() { ... }
}
Unit testing a class using DI (cont'd)

Service1 mock1 = Mockito.mock(Service1.class);
Service2 mock2 = Mockito.mock(Service2.class);
SubjectUnderTest sut = new SubjectUnderTest(
    mock1, mock2);

// configure mock
Mockito.when(mock1).service(eq("param"))
   .thenReturn("some return value);

// test your service
sut.doSomething();

// verify the results...
Testing bindings
Module.override to overcome problematic
bindings for testing:

Module testModule = Modules
    .override(new AcmeModule())
    .with(new AbstractModule() {
     @Override protected void configure() {
         bind(AuthChecker.class)
           .toInstance(new StubAuthChecker());
       }
     });


Avoid overriding too many bindings
System tests
System (end-to-end) tests a must with DI

Discover runtime failures in tests or your users
will discover them for you
5. Applying DI to existing code
Migrating to DI
Rewiring an existing code base to use DI will
take time

Don't need to use everywhere (but consistency
is good)
Techniques for migrating to DI
Approaches
● Bottom-up, separate injectors for subparts
● Top-down, single injector pushed down

Often need to compromise on ideal patterns
● Multiple Injectors
● Directly reference Injector
● Constructors do work
Things that make DI difficult: deep
inheritance
class BaseService { }
class MobileService extends BaseService { }
class AndroidService extends MobileService {
  @Inject AndroidService(Foo foo, Bar bar) {
    super(foo, bar);
}


What if BaseService needs to start depending
on Baz? Prefer composition.
Things that make DI difficult: static
methods
class SomeClass {
  static void process(Foo foo, Bar bar) {
    processFoo(foo); ...
  }
  static void processFoo(Foo foo) { ... }
}


I want to bind Foo and start injecting it
elsewhere. How do I get to Guice?
Thank you!

https://developers.google.com

Contact us:
  jedo@google.com
  nbehrens@google.com
Avoid @ImplementedBy

     @ImplementedBy(MySqlUserStore.class)
     interface UserStore { ... }


     ● Guice-ism, provided for convenience
     ● Interface should not have compile-time dep
       on specific implementation!
     ● Can be overridden by explicit bindings


Guice-specific
Constructor injection promotes
thread-safe code
@ThreadSafe
class Greeter {
   private final Greetings greetings;
   private final User user;

    @Inject
    Greeter(Greetings greetings, User user) {
       this.greetings = greetings;
       this.user = user;
    }

    String getGreeting(GreetingId greetingId) { ... }
}
Minimize Custom Scopes
● Adds complexity

● Scopes mismatches

  ○ e.g. request scoped type into singleton scoped type

● More (wrong) choices for developers

  ○ Should I put this in scopeA, scopeB, scopeC, ...?
Testing providers
If your module contains non-trivial provider
methods, unit test them

class FooModuleTest {
  void testBarWhenBarFlagDisabled() {
    assertNull(
        new FooModule().provideBar(
            false /* bar flag */);
  }

    void testBarWhenBarFlagEnabled() { ... }
}

Contenu connexe

Tendances

Rechecking SharpDevelop: Any New Bugs?
Rechecking SharpDevelop: Any New Bugs?Rechecking SharpDevelop: Any New Bugs?
Rechecking SharpDevelop: Any New Bugs?PVS-Studio
 
Dive into SObjectizer 5.5. Fifth part: Timers
Dive into SObjectizer 5.5. Fifth part: TimersDive into SObjectizer 5.5. Fifth part: Timers
Dive into SObjectizer 5.5. Fifth part: TimersYauheni Akhotnikau
 
Java script Techniques Part I
Java script Techniques Part IJava script Techniques Part I
Java script Techniques Part ILuis Atencio
 
Selenium my sql and junit user guide
Selenium my sql and junit user guideSelenium my sql and junit user guide
Selenium my sql and junit user guideFahad Shiekh
 
Club of anonimous developers "Refactoring: Legacy code"
Club of anonimous developers "Refactoring: Legacy code"Club of anonimous developers "Refactoring: Legacy code"
Club of anonimous developers "Refactoring: Legacy code"Victor_Cr
 
Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014
Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014
Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014First Tuesday Bergen
 
Implementation of fix messages for fix 5.0 sp2 and fixt1.1 specification
Implementation of fix messages for fix 5.0 sp2 and fixt1.1 specificationImplementation of fix messages for fix 5.0 sp2 and fixt1.1 specification
Implementation of fix messages for fix 5.0 sp2 and fixt1.1 specificationNeeraj Kaushik
 
softshake 2014 - Java EE
softshake 2014 - Java EEsoftshake 2014 - Java EE
softshake 2014 - Java EEAlexis Hassler
 
10 Typical Enterprise Java Problems
10 Typical Enterprise Java Problems10 Typical Enterprise Java Problems
10 Typical Enterprise Java ProblemsEberhard Wolff
 
Reactive Model-View-ViewModel Architecture
Reactive Model-View-ViewModel ArchitectureReactive Model-View-ViewModel Architecture
Reactive Model-View-ViewModel ArchitectureGyuwon Yi
 
Parsley & Flex
Parsley & FlexParsley & Flex
Parsley & Flexprideconan
 
Design pattern - part 1
Design pattern - part 1Design pattern - part 1
Design pattern - part 1Jieyi Wu
 
Introduction to Google Guice
Introduction to Google GuiceIntroduction to Google Guice
Introduction to Google GuiceKnoldus Inc.
 

Tendances (20)

Rechecking SharpDevelop: Any New Bugs?
Rechecking SharpDevelop: Any New Bugs?Rechecking SharpDevelop: Any New Bugs?
Rechecking SharpDevelop: Any New Bugs?
 
Dive into SObjectizer 5.5. Fifth part: Timers
Dive into SObjectizer 5.5. Fifth part: TimersDive into SObjectizer 5.5. Fifth part: Timers
Dive into SObjectizer 5.5. Fifth part: Timers
 
Bot builder v4 HOL
Bot builder v4 HOLBot builder v4 HOL
Bot builder v4 HOL
 
Java script Techniques Part I
Java script Techniques Part IJava script Techniques Part I
Java script Techniques Part I
 
Selenium my sql and junit user guide
Selenium my sql and junit user guideSelenium my sql and junit user guide
Selenium my sql and junit user guide
 
Club of anonimous developers "Refactoring: Legacy code"
Club of anonimous developers "Refactoring: Legacy code"Club of anonimous developers "Refactoring: Legacy code"
Club of anonimous developers "Refactoring: Legacy code"
 
Dependency Injection for Android
Dependency Injection for AndroidDependency Injection for Android
Dependency Injection for Android
 
Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014
Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014
Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014
 
C++ aptitude
C++ aptitudeC++ aptitude
C++ aptitude
 
Implementation of fix messages for fix 5.0 sp2 and fixt1.1 specification
Implementation of fix messages for fix 5.0 sp2 and fixt1.1 specificationImplementation of fix messages for fix 5.0 sp2 and fixt1.1 specification
Implementation of fix messages for fix 5.0 sp2 and fixt1.1 specification
 
Spring 3 to 4
Spring 3 to 4Spring 3 to 4
Spring 3 to 4
 
Migrating to JUnit 5
Migrating to JUnit 5Migrating to JUnit 5
Migrating to JUnit 5
 
softshake 2014 - Java EE
softshake 2014 - Java EEsoftshake 2014 - Java EE
softshake 2014 - Java EE
 
10 Typical Enterprise Java Problems
10 Typical Enterprise Java Problems10 Typical Enterprise Java Problems
10 Typical Enterprise Java Problems
 
Reactive Model-View-ViewModel Architecture
Reactive Model-View-ViewModel ArchitectureReactive Model-View-ViewModel Architecture
Reactive Model-View-ViewModel Architecture
 
Jersey Guice AOP
Jersey Guice AOPJersey Guice AOP
Jersey Guice AOP
 
Parsley & Flex
Parsley & FlexParsley & Flex
Parsley & Flex
 
Unit testing concurrent code
Unit testing concurrent codeUnit testing concurrent code
Unit testing concurrent code
 
Design pattern - part 1
Design pattern - part 1Design pattern - part 1
Design pattern - part 1
 
Introduction to Google Guice
Introduction to Google GuiceIntroduction to Google Guice
Introduction to Google Guice
 

Similaire à Devoxx 2012 (v2)

CDI e as ideias pro futuro do VRaptor
CDI e as ideias pro futuro do VRaptorCDI e as ideias pro futuro do VRaptor
CDI e as ideias pro futuro do VRaptorCaelum
 
Jug Guice Presentation
Jug Guice PresentationJug Guice Presentation
Jug Guice PresentationDmitry Buzdin
 
Daggerate your code - Write your own annotation processor
Daggerate your code - Write your own annotation processorDaggerate your code - Write your own annotation processor
Daggerate your code - Write your own annotation processorBartosz Kosarzycki
 
Thomas braun dependency-injection_with_robo_guice-presentation-final
Thomas braun dependency-injection_with_robo_guice-presentation-finalThomas braun dependency-injection_with_robo_guice-presentation-final
Thomas braun dependency-injection_with_robo_guice-presentation-finalDroidcon Berlin
 
Google Web Toolkits
Google Web ToolkitsGoogle Web Toolkits
Google Web ToolkitsYiguang Hu
 
Testing in android
Testing in androidTesting in android
Testing in androidjtrindade
 
4Developers: Dominik Przybysz- Message Brokers
4Developers: Dominik Przybysz- Message Brokers4Developers: Dominik Przybysz- Message Brokers
4Developers: Dominik Przybysz- Message BrokersPROIDEA
 
Building maintainable app #droidconzg
Building maintainable app #droidconzgBuilding maintainable app #droidconzg
Building maintainable app #droidconzgKristijan Jurković
 
It's complicated, but it doesn't have to be: a Dagger journey
It's complicated, but it doesn't have to be: a Dagger journeyIt's complicated, but it doesn't have to be: a Dagger journey
It's complicated, but it doesn't have to be: a Dagger journeyThiago “Fred” Porciúncula
 
JVM Mechanics: When Does the JVM JIT & Deoptimize?
JVM Mechanics: When Does the JVM JIT & Deoptimize?JVM Mechanics: When Does the JVM JIT & Deoptimize?
JVM Mechanics: When Does the JVM JIT & Deoptimize?Doug Hawkins
 
Architecting your GWT applications with GWT-Platform - Lesson 02
Architecting your GWT applications with GWT-Platform - Lesson 02Architecting your GWT applications with GWT-Platform - Lesson 02
Architecting your GWT applications with GWT-Platform - Lesson 02rhemsolutions
 
Stopping the Rot - Putting Legacy C++ Under Test
Stopping the Rot - Putting Legacy C++ Under TestStopping the Rot - Putting Legacy C++ Under Test
Stopping the Rot - Putting Legacy C++ Under TestSeb Rose
 
Binding business data to vaadin components
Binding business data to vaadin componentsBinding business data to vaadin components
Binding business data to vaadin componentsPeter Lehto
 

Similaire à Devoxx 2012 (v2) (20)

CDI e as ideias pro futuro do VRaptor
CDI e as ideias pro futuro do VRaptorCDI e as ideias pro futuro do VRaptor
CDI e as ideias pro futuro do VRaptor
 
Jug Guice Presentation
Jug Guice PresentationJug Guice Presentation
Jug Guice Presentation
 
Google GIN
Google GINGoogle GIN
Google GIN
 
CDI in JEE6
CDI in JEE6CDI in JEE6
CDI in JEE6
 
Daggerate your code - Write your own annotation processor
Daggerate your code - Write your own annotation processorDaggerate your code - Write your own annotation processor
Daggerate your code - Write your own annotation processor
 
Thomas braun dependency-injection_with_robo_guice-presentation-final
Thomas braun dependency-injection_with_robo_guice-presentation-finalThomas braun dependency-injection_with_robo_guice-presentation-final
Thomas braun dependency-injection_with_robo_guice-presentation-final
 
Google Web Toolkits
Google Web ToolkitsGoogle Web Toolkits
Google Web Toolkits
 
CDI @javaonehyderabad
CDI @javaonehyderabadCDI @javaonehyderabad
CDI @javaonehyderabad
 
Guice gin
Guice ginGuice gin
Guice gin
 
Testing in android
Testing in androidTesting in android
Testing in android
 
Sapphire Gimlets
Sapphire GimletsSapphire Gimlets
Sapphire Gimlets
 
4Developers: Dominik Przybysz- Message Brokers
4Developers: Dominik Przybysz- Message Brokers4Developers: Dominik Przybysz- Message Brokers
4Developers: Dominik Przybysz- Message Brokers
 
Building maintainable app #droidconzg
Building maintainable app #droidconzgBuilding maintainable app #droidconzg
Building maintainable app #droidconzg
 
It's complicated, but it doesn't have to be: a Dagger journey
It's complicated, but it doesn't have to be: a Dagger journeyIt's complicated, but it doesn't have to be: a Dagger journey
It's complicated, but it doesn't have to be: a Dagger journey
 
JVM Mechanics: When Does the JVM JIT & Deoptimize?
JVM Mechanics: When Does the JVM JIT & Deoptimize?JVM Mechanics: When Does the JVM JIT & Deoptimize?
JVM Mechanics: When Does the JVM JIT & Deoptimize?
 
Architecting your GWT applications with GWT-Platform - Lesson 02
Architecting your GWT applications with GWT-Platform - Lesson 02Architecting your GWT applications with GWT-Platform - Lesson 02
Architecting your GWT applications with GWT-Platform - Lesson 02
 
Stopping the Rot - Putting Legacy C++ Under Test
Stopping the Rot - Putting Legacy C++ Under TestStopping the Rot - Putting Legacy C++ Under Test
Stopping the Rot - Putting Legacy C++ Under Test
 
Building maintainable app
Building maintainable appBuilding maintainable app
Building maintainable app
 
Binding business data to vaadin components
Binding business data to vaadin componentsBinding business data to vaadin components
Binding business data to vaadin components
 
SOLID Principles
SOLID PrinciplesSOLID Principles
SOLID Principles
 

Devoxx 2012 (v2)

  • 1. Effective Dependency Injection Nicholas Behrens and Jerome Dochez Google Inc.
  • 2. This talk 1. Quick DI refresher 2. Guidelines for effective DI 3. Composing applications using DI 4. Testing code using DI 5. Applying DI to existing code
  • 3. 1. Quick DI Refresher
  • 4. DI = Dependency Injection Declare what you need, not how to get it PaymentProcessor( CreditCardService creditCardService) { this.creditCardService = creditCardService; this.riskAssessor = new RiskAssessorImpl(...); }
  • 5. DI = Dependency Injection + Loose coupling, facilitate reuse and testing - Complexity, runtime failures Many frameworks with many tools Important to establish good conventions
  • 6. DI Framework ● Providers ● Bindings ● @Inject ● Module [= collection of Bindings] ● Injector [= collection of Modules] ○ injector.getInstance(type) ● Scopes
  • 7. 2. Guidelines for effective DI
  • 8. Explicit > Implicit ● Ensure explicit bindings for all injected types ○ bind(RequestFilter.class); ● Implicit bindings dangerous ○ Removing an explicit binding ○ Pulling in undesired explicit binding
  • 9. Inject exactly what you need Bad: PaymentProcessor(User user) { this.account = user.getAccount(); } Better: PaymentProcessor(Account account) { this.account = account; }
  • 10. Prefer constructor injection ● private final fields ● Immutability, thread-safety ● Objects valid post-construction ● Match developer expectations
  • 11. Avoid work in constructors ● Separation of concerns, facilitates testing, "standard" constructor ● In Guice: runtime ProvisionException easily propagates up to top level ● Initialize after constructing object graph
  • 12. If you must do work in a constructor interface DataStoreProvider<T> extends CheckedProvider<T> { T get() throws DataStoreException; } Guice-specific
  • 13. If you must do work in a constructor (cont'd) @CheckedProvides(DataStoreProvider.class) Contacts provideContacts(UserId userId, DataStore dataStore) throws DataStoreException { return dataStore.readUserContacts(userId); } @Override protected void configure() { install(ThrowingProviderBinder.forModule(this)); } Guice-specific
  • 14. If you must do work in a constructor (cont'd) @Inject ContactsFormatter( DataStoreProvider<Contacts> contactsProvider) { this.contactsProvider = contactsProvider; } String formatContacts() throws DataStoreException { Contacts contacts = contactsProvider.get(); // throws ... } Guice-specific
  • 15. If you must do work in a constructor (cont'd) Problems ● More complex, verbose bindings ● More setup work for tests ● Business logic creep ● Viral Guice-specific
  • 16. Avoid direct deps on Injector ● No longer declaring specific deps ● Injector allows dep on anything ● Easy to start using Injector, hard to stop
  • 17. Avoid binding very general types Bad bindConstant().to(8080); @Inject int port; Better bindConstant().annotatedWith(ForHttp.class) .to(8080); @Inject @ForHttp int port;
  • 18. Avoid parameterized binding annotations At first it seems convenient... @Named("http") int httpPort; ...but then you do @Named("HTTP") int httpPort; No help from the IDE, more runtime errors. Harder to track down injection sites.
  • 19. Using parameterized binding annotations safely Consistently use constants (enums!) for parameters: @ForPort(Ports.HTTP) int httpPort; @FeatureEnabled(Features.X) boolean xEnabled;
  • 20. Keep modules fast and side-effect free Make it easy to compose modules Avoid ● bind().toInstance(...) ● requestStaticInjection(...) ● Serial eager initialization of objects Guice-specific
  • 21. Prefer Null Objects over @Nullable @Provides AbuseService provideAbuseService( @ForAbuse String abuseServiceAddress) { if (abuseServiceAddress.isEmpty()) { return null; } return new AbuseServiceImpl(abuseServiceAddress); } class AbuseChecker { @Inject AbuseChecker( @Nullable AbuseService abuseService) { ... } void doAbuseCheck(...) { if (abuseService != null) { ... ) } Bind StubAbuseService instead of null. Simplify AbuseChecker.
  • 22. Use Provider to avoid unnecessary provisioning @Provides AbuseService provideAbuseService( @UseNewAbuseService boolean useNewAbuseService, Provider<OldAbuseServiceImpl> oldAbuseService, Provider<NewAbuseServiceImpl> newAbuseService) { return useNewAbuseService.get() ? newAbuseService.get() : oldAbuseService.get(); }
  • 23. Scoping as an alternative to context public interface PipelineVisitor() { void visit(TypeToVisit typeToVisit); } public class Pipeline() { private final PipelineVisitor[] visitors = { new Visitor1(), new Visitor2(), ... } public void visit(TypeToVisit typeToVisit) { ... } }
  • 24. Requirements creep in, you add more parameters... public interface PipelineVisitor() { void visit(TypeToVisit typeToVisit, User user); } public interface PipelineVisitor() { void visit(TypeToVisit typeToVisit, User user, Logger logger); } public interface PipelineVisitor() { void visit(TypeToVisit typeToVisit, User user, Logger logger, RequestParams params); } and so on...
  • 25. Ah yes, the Context to the rescue public interface PipelineVisitor() { void visit(PipelineContext context); } public Class PipelineContext { TypeToVisit typeToVisit; User user; Logging logging; ... and many more ... }
  • 26. and adapts the Pipeline itself. public class Pipeline() { private final PipelineVisitor[] visitors = { ... }; OutputType visit(TypeToVisit in, User user, Logging log) { OutputType out = new OutputType(); PipelineContext context = new PipelineContext(in, user, log); for (PipelineVisitor visitor : visitors) { visitor.visit(context); } } } what about testing, isolation,...
  • 27. First : remove the context public interface Visitor { void visit(TypeToVisit in); } public class LoggerVisitor { private final Logger logger; @Inject public LoggerVisitor(Logger logger) {...} void visit(InputType in, Output out) { logger.log(Level.INFO, "Visiting " + in); } public class UserDependentVisitor { private final User user; ... }
  • 28. Second : use a Multi-Binder class VisitorsModule extend AbstractModule { @Override protected void configure() { Multibinder<PipelineVisitor> visitorBinder = Multibinder.newSetBinder( binder(), PipelineVisitor.class); visitorBinder.addBinding().to(LoggerVisitor.class); visitorBinder.addBinding() .to(UserDependentVisitor.class); } Now we can inject a Set<Visitor>
  • 29. Inject into Pipeline public class Pipeline() { private final Set<Provider<PipelineVisitor.class>> visitors @Inject Pipeline(Set<Provider<PipelineVisitor.class>> visitors) { this.visitors = visitors; } public void visit(TypeToVisit in) { for (Provider<PipelineVisitor.class> visitor : visitors) { visitor.get().visit(in); ...
  • 30. Testing Visitors and Pipeline public void testVisitor() { Visitor v = new FirstVisitor(...); v.visit(in); assert(...) } public void testPipeline() { Visitor v = Mockito.mock(Visitor.class); Pipeline p = new Pipeline(Sets.of(Providers.of(v))); TypeToVisit in = new TypeToVisit(...); p.visit(in); Mockito.verify(v).visit(eq(in), any(Output.class)); }
  • 32. Modular Java! Use a Service-Based Architecture ● All services defined as an interface ● Implementations package-private ● Package-level Module binds No need for OSGi unless you need: ● Runtime extensibility ● Runtime versioning
  • 33. Modular Java: Declare API and Impl API: (interfaces, enums, binding annotations) public interface SpamChecker { void checkIsSpam(...); } Implementation: (package private) class SpamCheckerImpl implements SpamChecker { void checkIsSpam(...) { ... } }
  • 34. Modular Java: Colocate bindings In the same package as SpamChecker*: public class SpamCheckerModule extends AbstractModule { @Override public void configure() { bind(SpamCheckerImpl.class).in(Singleton.class); bind(SpamChecker.class).to(SpamCheckerImpl.class); } } To use in your app, install SpamCheckerModule and then inject SpamChecker as needed.
  • 35. Modular Java: Document deps Document what your module requires: class SpamCheckerModule ... { @Override protected void configure() { requireBinding(Clock.class); requireBinding(CryptoService.class); ... } }
  • 36. Modular Java: Package it all up 1. Maven a. All public interfaces in -api.jar module b. All implementations and modules in -impl.jar c. api module should only be used for compiling dependent modules, impl module should be used for assembling the application 2. OSGi a. Export interfaces and Module only b. Avoid versioning at runtime
  • 37. 4. Testing code using DI
  • 38. Unit testing a class using DI public class SubjectUnderTest { @Inject SubjectUnderTest(Service1 s1, Service2 s2) { // Assign to fields } String doSomething() { ... } }
  • 39. Unit testing a class using DI (cont'd) Service1 mock1 = Mockito.mock(Service1.class); Service2 mock2 = Mockito.mock(Service2.class); SubjectUnderTest sut = new SubjectUnderTest( mock1, mock2); // configure mock Mockito.when(mock1).service(eq("param")) .thenReturn("some return value); // test your service sut.doSomething(); // verify the results...
  • 40. Testing bindings Module.override to overcome problematic bindings for testing: Module testModule = Modules .override(new AcmeModule()) .with(new AbstractModule() { @Override protected void configure() { bind(AuthChecker.class) .toInstance(new StubAuthChecker()); } }); Avoid overriding too many bindings
  • 41. System tests System (end-to-end) tests a must with DI Discover runtime failures in tests or your users will discover them for you
  • 42. 5. Applying DI to existing code
  • 43. Migrating to DI Rewiring an existing code base to use DI will take time Don't need to use everywhere (but consistency is good)
  • 44. Techniques for migrating to DI Approaches ● Bottom-up, separate injectors for subparts ● Top-down, single injector pushed down Often need to compromise on ideal patterns ● Multiple Injectors ● Directly reference Injector ● Constructors do work
  • 45. Things that make DI difficult: deep inheritance class BaseService { } class MobileService extends BaseService { } class AndroidService extends MobileService { @Inject AndroidService(Foo foo, Bar bar) { super(foo, bar); } What if BaseService needs to start depending on Baz? Prefer composition.
  • 46. Things that make DI difficult: static methods class SomeClass { static void process(Foo foo, Bar bar) { processFoo(foo); ... } static void processFoo(Foo foo) { ... } } I want to bind Foo and start injecting it elsewhere. How do I get to Guice?
  • 47. Thank you! https://developers.google.com Contact us: jedo@google.com nbehrens@google.com
  • 48.
  • 49.
  • 50. Avoid @ImplementedBy @ImplementedBy(MySqlUserStore.class) interface UserStore { ... } ● Guice-ism, provided for convenience ● Interface should not have compile-time dep on specific implementation! ● Can be overridden by explicit bindings Guice-specific
  • 51. Constructor injection promotes thread-safe code @ThreadSafe class Greeter { private final Greetings greetings; private final User user; @Inject Greeter(Greetings greetings, User user) { this.greetings = greetings; this.user = user; } String getGreeting(GreetingId greetingId) { ... } }
  • 52. Minimize Custom Scopes ● Adds complexity ● Scopes mismatches ○ e.g. request scoped type into singleton scoped type ● More (wrong) choices for developers ○ Should I put this in scopeA, scopeB, scopeC, ...?
  • 53. Testing providers If your module contains non-trivial provider methods, unit test them class FooModuleTest { void testBarWhenBarFlagDisabled() { assertNull( new FooModule().provideBar( false /* bar flag */); } void testBarWhenBarFlagEnabled() { ... } }