Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Brief introduction about Dependency Injection

Brief introduction about Dependency Injection

Dependency injection design pattern is introduced in order to solve some common issues due to explicit wiring between a component and its collaborator or service.
Spring Framework and AngularJS are involved to show how dependency injection could simplify the way of writing code but above all testing.
Clean and concise code is obtained using DI. Real example are provided on Github and Plunkr.

Eugenio Lentini

August 10, 2014
Tweet

More Decks by Eugenio Lentini

Other Decks in Programming

Transcript

  1. 0A pre-dependency injection Base example Advantages • easily enable testability

    • clean and concise code • testing client and service in isolation • client no need to know how to build the dependency object graph
  2. 0A pre-dependency injection Encapsulate creation 1 public class Emailer {

    2 3 private SpellChecker spellChecker; 4 5 public Emailer() { 6 this.spellChecker = new SpellChecker(); 7 } 8 9 public void send(String text) { 10 //some implementation 11 } 12 } How to test? • Impossible: MockSpellChecker cannot substitute the internal spellChecker. Change implementation? • Impossible: Emailer encapsulates the creation of its dependencies.
  3. 0A pre-dependency injection Construction by hand 1 public class Emailer

    { 2 3 private SpellChecker spellChecker; 4 5 public void send(String text) { 6 //some implementation 7 } 8 9 public void setSpellChecker(SpellChecker spellChecker) { 10 this.spellChecker = spellChecker; 11 } 12 } Pros • Setter method: different flavors of the service can be injected. Cons • Object construction knowledge: on the client of the service. • Repeat wiring code • What about: encapsulation principle and altering the object graph?
  4. 0A pre-dependency injection Construction by hand test Pros • Setter

    method: MockSpellChecker can be easily injected. • Check if dependency has been correctly used. Cons • Repeat wiring code 1 @Test 2 public void testEmailerCheckSpelling() throws Exception { 3 4 MockSpellChecker mockSpellChecker = new MockSpellChecker(); 5 6 Emailer emailer = new Emailer(); 7 8 emailer.setSpellChecker(mockSpellChecker); 9 emailer.send("Hello World"); 10 11 assert mock.isSpellCheckingDone(); 12 }
  5. 0A pre-dependency injection Factory pattern Pros • Level of abstraction:

    Emailer client code is separated from the Emailer creation. • No internal knowledge: client code only use a specific factory to obtain a dependency. • Code clean and concise even with richer and more complex object graph. Cons • Extreme growth: factory classes tend to grow becoming really big and uncontrolled. 1 public class EmailerFactory { 2 3 public Emailer newFrenchEmailer() { 4 return new Emailer(new FrenchSpellChecker()); 5 } 6 7 public Emailer newJapaneseEmailer() { 8 return new Emailer(new JapaneseSpellChecker()); 9 } 10 } 11 12 //somewhere in the Client code 13 Emailer emailer = new EmailerFactory().newFrenchEmailer();
  6. 0A pre-dependency injection Factory pattern test Pros • Level of

    abstraction: Emailer client code is separated from the Emailer creation. • No internal knowledge: client code only use a specific factory to obtain a dependency. • Code clean and concise even with richer and more complex object graph. Cons • Extreme growth and maintenance: factory classes tend to grow becoming really big and uncontrolled. 1 public class EmailerFactory { 2 3 public Emailer newFrenchEmailer() { 4 return new Emailer(new FrenchSpellChecker()); 5 } 6 7 public Emailer newJapaneseEmailer() { 8 return new Emailer(new JapaneseSpellChecker()); 9 } 10 } 11 12 //somewhere in the Client code 13 Emailer emailer = new EmailerFactory().newFrenchEmailer();
  7. 0A pre-dependency injection Factory pattern test 1 public class EmailClient

    { 2 private Emailer emailer = new EmailerFactory.newEnglishEmailer(); 3 4 public void run() { 5 emailer.send("Hello World"); 6 } 7 } 8 9 @Test 10 public void testEmailClient() { 11 MockEmailer mock = new MockEmailer(); 12 EmailerFactory.set(mock); 13 14 new EmailClient.run(); 15 16 assert mock.correctlySent(); 17 } How & Cons to test with factory • Heavily modify factory class code to support testing for any client, state is shared thus code cleanup required after each test. • Create a factory mock or stub which can easily grow, it must accompany every variation of every service.
  8. 0B dependency injection Hollywood Principle: don’t call us; we’ll call

    you! Enable • Unawareness: client not aware of the Emailer kind. • Reversal of responsibilities: service not explicitly required. Pros • Easily enable testability via setter method or constructor injection (construction by hand). • Client no needs to know its dependencies and how to build them (factory pattern). • Dependencies are not required but provided (new). 1 public class Emailer { 2 public void send(String text) { 3 //some implementation 4 } 5 //some other methods 6 } 7 8 public class SimpleEmailClient { 9 private Emailer emailer; 10 11 public SimpleEmailClient(Emailer emailer) { 12 this.emailer = emailer; 13 } 14 public void sendEmail() { 15 emailer.send(readMessage()); 16 } 17 }
  9. 0B dependency injection Hollywood Principle: don’t call us; we’ll call

    you! embodies the Hollywood Principle Dependency Injector • Control over construction, wiring and assembly dependency or object graph. • Separates infrastructure code (wiring and construction) from application code (core purpose of the service)
  10. 0C dependency injection with Spring Spring Framework Spring • Open

    source application framework and inversion of control container for Java platform. • 1 October 2002, release 1.0.0, 7 July 2014, release 4.0.6, now 4.1.0 RC1 • active development and evolution to meet new programming requirements • Developed by Pivotal, a spin-out and joint venture of EMC Corporation and VMware. • Excellent documentation, clean and tested code, wide community and support. Projects • Spring Framework • core support for dependency injection, transaction management • Spring Data • provide consistent approach to data access • relational, non-relational, map-reduce and much more • Spring XD • simplify the development of big data applications • address analytics, batch jobs and data export • much more…
  11. 0C dependency injection with Spring Example: the client of the

    service Client • SimpleEmailClient decoupled from the EmailerService implementation. • SimpleEmailClient unaware of which implementation will be provided at runtime, programming against interfaces (OOP principle). 1 @Component 2 public class SimpleEmailClient { 3 4 final private EmailerService service; 5 6 @Autowired 7 public SimpleEmailClient(EmailerService service) { 8 this.service = service; 9 } 10 11 public void printMessage() { 12 System.out.println(this.service.getMessage()); 13 } 14 }
  12. 1 @Configuration 2 @ComponentScan 3 public class Application { 4

    5 @Bean 6 MessageService mockEmailerService() { 7 return new EmailerService() { 8 public String getMessage() { 9 return "Hello World!"; 10 } 11 }; 12 } 13 14 public static void main(String[] args) { 15 ApplicationContext context = 16 new AnnotationConfigApplicationContext(Application.class); 17 SimpleEmailClient client = context.getBean(SimpleEmailClient.class); 18 client.printMessage(); 19 } 20 } 0C dependency injection with Spring Example: the service and the mock Injected mock implements the interface Injector wires client with its dependency
  13. 0C dependency injection with Spring Another example with integration test

    1 @Entity 2 @Table(name = "MOVIE") 3 public class Movie extends AbstractEntity { 4 5 @Column(name = "TITLE") 6 private String title; 7 8 protected Movie() {...} 9 } Movie example • Simple Movie class annotated JPA 2.0, Hibernate, store and retrieve. • A service interface is given. • Repository and service should be created and implemented to support basic CRUD operations. Code on Github • The code of the example is available on Github 1 public interface MovieService { 2 3 List<Movie> findAll(); 4 5 Movie create(final Movie movie); 6 7 Movie delete(final Movie movie); 8 9 Movie update(final Movie movie); 10 }
  14. 0C dependency injection with Spring Integration test: another example Movie

    repository and service • No need to implement basic CRUD methods as findAll(), delete(), save() etc. • Methods are resolved based on attribute names, just declare in the repository interface findById() to have it automatically implemented at runtime where Id is the attribute. • More complex methods have to be directly implemented. • Clean and concise code, no need to implement explicit SQL query, reduce boiler-plate code. 1 @Repository 2 @Transactional 3 public class MovieServiceImpl implements MovieService { 4 5 @Autowired 6 private MovieRepository movieRepository; 7 8 @Override 9 public final List<Movie> findAll() { 10 return Lists.newArrayList(movieRepository.findAll()); 11 } 12 ... 13 } 1 public interface MovieRepository extends CrudRepository<Movie, Long> { 2 }
  15. 0C dependency injection with Spring Integration test: another example 1

    @ActiveProfiles("test") 2 public class MovieServiceImplTest extends AbstractServiceImplTest { 3 4 5 @Autowired 6 private MovieService movieService; 7 8 @DataSets(setUpDataSet = "/MovieServiceImplTest.xls") 9 @Test 10 public final void testFindAll() throws Exception { 11 List<Movie> results = movieService.findAll(); 12 13 assertNotNull(results); 14 assertThat(results.size(), equalTo(3)); 15 } 16 } Injected implementation based on the interface Build ApplicationContext based on the active profile Insert into the data source tables from xls file before the method execution. After method execution automatic rollback.
  16. 1 @RunWith(SpringJUnit4ClassRunner.class) 2 @ContextConfiguration(classes = {ServiceTestConfig.class}) 3 @TestExecutionListeners({ServiceTestExecutionListener.class}) 4 @ActiveProfiles("test")

    5 public abstract class AbstractServiceImplTest extends AbstractTransactionalJUnit4SpringContextTests { 6 7 @PersistenceContext 8 protected EntityManager em; 9 } 0C dependency injection with Spring ApplicationContext configuration with a specific data source, schema and data. Based on a standard JUnit Runner. Standard javax.persistence, hibernate jpa 2.0 api. Listener to intercept standard test execution lifecycle. Test • Non invasive solution to build integration tests based on standard frameworks like JUnit and Hibernate • It works on top of standard frameworks providing clean and concise code for complex test cases. Integration test: another example
  17. 1 @Configuration 2 @ImportResource("classpath:datasource-tx-jpa.xml") 3 @ComponentScan(basePackages = {"com.contrastofbeauty.movieapp.service"}) 4 @Profile("test")

    5 public class ServiceTestConfig { 6 7 @Bean 8 public DataSource dataSource() { 9 return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .addScript("classpath:schema.sql").build(); 10 } 11 12 @Bean(name = "databaseTester") 13 public DataSourceDatabaseTester dataSourceDatabaseTester() { 14 DataSourceDatabaseTester databaseTester = new DataSourceDatabaseTester(dataSource()); 15 return databaseTester; 16 } 17 18 @Bean(name = "xlsDataFileLoader") 19 public XlsDataFileLoader xlsDataFileLoader() { 20 return new XlsDataFileLoader(); 21 } 22 } 0C dependency injection with Spring Reuse transaction and JPA configuration DbUnit classes to load xls files and setup the data source Data source configuration Integration test: another example
  18. 0C dependency injection with Spring Considerations Spring Debate • Spring

    is not standard. • Just pointless, Spring promote the use of standard. • Really important to have a standard, but if the solution of your problem is not standard you will wait three or more years? • Spring Social (JSR-357 rejected in 2012, really opened debate on future) • Widespread container testing done with Arquillan, JBoss specific. • Spring is not compatible. • Free to use JPA, CDI annotations etc. • Use all Java EE technologies and additional features provide by Spring. • Spring works with all Java EE application servers and more like Jetty and Tomcat. • Spring is a must, best choice • Not always and Just pointless, what are your requirements? • Spring is a meta-framework, highly scalable, highly configurable but complex. • Spring configuration could be difficult to manage when the project grows. • Use only Spring dependency injection? May be consider what already provided by the container.
  19. 0D AngularJS $scope, $controller, $http and test with mocks Where

    to find the code • Github for source code: angulajs-popcorn • Plunker online live editing: application and testing
  20. 0D AngularJS Definition MVC Design Pattern • View is projection

    of the model through the HTML template. • Controller is a Javascript constructor function used to augment the Angular scope. • Scope an object referring to application model, used along with data model, template and controller to keep models and views in sync. Template Model View (model/view data binding) Root Scope movieController Scope movies: Array <!DOCTYPE html> <html lang="en" ng-app="movieApp"> <body ng-controller="movieController"> <ul> <li ng-repeat="movie in movies"> {{movie.title}} </li> </ul> </body> </html> Repetear Scope movie: Object ▪ Commando ▪ Raw Deal ▪ Predator n-times ng-controller ng-app n-times
  21. 0D AngularJS dependency injection Definition Angular Injection • $scope injected

    into controller, scope is the glue between application controller and the view. • $http injected, an Angular service to facilitate the communication with the remote HTTP server. • $httpBackend mock of the real $httpBackend, backend used by the service to delegate to XMLHttpRequest. 1 var movieApp = angular.module('movieApp', ['xeditable', 'ngMockE2E']); 2 3 movieApp.controller('MovieController', function($scope, $http) { 4 5 $scope.loadMovies = function() { 6 return $http.get("/movies").success(function(response){ 7 $scope.movies = response; 8 }); 9 }; 10 }); 11 12 // --------------- mock $http requests ---------------------- 13 movieApp.run(function($httpBackend) { 14 $httpBackend.whenGET('/movies').respond([ 15 {id: 1, title: "Commando", actors: "Arnold Schwarzenegger"}, 16 {id: 2, title: "Raw Deal", actors: "Arnold Schwarzenegger"}, 17 {id: 3, title: "Predator", actors: "Arnold Schwarzenegger"} 18 ]) 19 });
  22. 0D AngularJS dependency injection Test controller using Jasmine BDD framework

    1 describe('Movie Controller test suite', function() { 2 3 describe('MovieController', function() { 4 5 var $scope, $httpBackend, createController, $http; 6 7 beforeEach(module('movieApp')); 8 9 beforeEach(inject(function($controller, $rootScope, _$httpBackend_, _$http_) { 10 $scope = $rootScope.$new(); 11 $httpBackend = _$httpBackend_; 12 $http = _$http_; 13 14 createController = function() { 15 return $controller('MovieController', { 16 $scope: $scope 17 }); 18 }; 19 })); 20 21 it("should GET all the movies", function() { 22 $httpBackend.expectGET('/movies').respond(200, [{}, {}, {}]); 23 createController(); 24 $scope.loadMovies(); 25 $httpBackend.flush(); 26 expect($scope.movies.length).toBe(3); 27 }); 28 }); Jasmine description suite Setup test context injecting Angular services Jasmine test declaration or spec Obtain the controller and invoke the function to test Assert the result
  23. 0D AngularJS Considerations Advantages • clean separation of concerns •

    template is pure HTML, no other languages • injection and made for testing • two-way data binding, change in the view is reflected in the model and viceversa • directives, markers on the DOM element to attach specific behavior or transform the element (ng-repeat) • powerful and scalable to support big projects • really well documented, excellent support, big community, fast growing set of libraries Disadvantages • learning curve, takes time • best practices needed to avoid bad programming patterns i.e. code can be written inside of templates • complexity, some parts are a bit complex to understand
  24. 0D Bibliography Dependency Injection • Martin Fowler’s blog • Inversion

    of Control • SpringFramework • Angular • Google Guice • JavaEE CDI