Clean code via Dependency Injection + Guice                                @jordi9bit.ly/devfest12cleancode   9 noviembre ...
... shameless self-promotion bro!
About youGang of Four?Legacy Code?Dependency Injection?Monkey patching?Monkey testing?Unit testing?TDD / BDD?Agile? XP? Sc...
S.O.L.I.D.Single responsibility principleOpen/closed principleLiskov substitution principleInterface segregation principle...
Legacy code is code without test
F.I.R.S.T.Fast Run hundreds or thousands per secondIsolated Failure reasons become obviousReliable Run repeatedly in any o...
Write some code, kill technical debt                             (or not)
Showtime!class Showtimes {  MovieRepository repository;    Showtimes() {      repository = MovieRepositoryFactory.getInsta...
Smell: Constructor does real workclass Showtimes {  MovieRepository repository;    Showtimes() {      repository = MovieRe...
Smell: Constructor does real workclass Showtimes {  MovieRepository repository;    Showtimes() {      repository = MovieRe...
Smell: Constructor does real workclass Showtimes {  MovieRepository repository;    Showtimes() {      repository = MovieRe...
Factory Pattern, boilerplatepublic class MovieRepositoryFactory {  static class MovieRepositoryHolder {    static MovieRep...
Factory Pattern, boilerplatepublic class MovieRepositoryFactory {  static class MovieRepositoryHolder {    static MovieRep...
Factory Pattern, boilerplatepublic class MovieRepositoryFactory {  static class MovieRepositoryHolder {    static MovieRep...
Factory Pattern, boilerplatepublic class MovieRepositoryFactory {  static class MovieRepositoryHolder {    static MovieRep...
Factory Pattern, boilerplatepublic class MovieRepositoryFactory {  static class MovieRepositoryHolder {    static MovieRep...
Factory Pattern, boilerplatepublic class MovieRepositoryFactory {  static class MovieRepositoryHolder {    static MovieRep...
Hard to test@Testvoid findMovies_cantTestYouDude() {  Showtimes showtimes = new Showtimes();  // FRAK!  // No database set...
Fix: Constructor does real work
Fix: Constructor does real workclass Showtimes {  MovieRepository repository;    Showtimes(MovieRepository repository) {  ...
Fix: Constructor does real workclass Showtimes {  // Hollywood principle: "Dont call us, well call you"  MovieRepository r...
Fix: Constructor does real workclass Showtimes {  // Hollywood principle: "Dont call us, well call you"  MovieRepository r...
Fix: MovieRepository creation?class App {  public static void main(String[] args) {    // Manual Dependency Injection... b...
Fix: MovieRepository creationclass App {  public static void main(String[] args) {    Injector injector = Guice.createInje...
Fix: MovieRepository creationclass App {  public static void main(String[] args) {    Injector injector = Guice.createInje...
Fix: MovieRepository creationclass App {  public static void main(String[] args) {     Injector injector = Guice.createInj...
Fix: Testable and flexible designShowtimes showtimes;@Mock MovieRepository repository;@Test void findMovies_easyNow() {  s...
Fix: Testable and flexible designShowtimes showtimes; //SUT@Mock MovieRepository repository;@Test void findMovies_easyNow(...
Fix: Testable and flexible designShowtimes showtimes; //SUT@Mock MovieRepository repository; // <3 Mockito, BDD@Test void ...
Fix: Testable and flexible designShowtimes showtimes; //SUT@Mock MovieRepository repository; // <3 Mockito, BDD@Test void ...
Fix: Testable and flexible designShowtimes showtimes; //SUT@Mock MovieRepository repository; // <3 Mockito, BDD@Test void ...
Constructor does real work,more flavours
Flavour: Partial objects, Law of Demeterclass Ratings {  User user;    Ratings(AccountManager manager) {      user = manag...
Flavour: Partial objects, Law of Demeterclass Ratings {  User user;    Ratings(AccountManager manager) {      user = manag...
Fix: Partial objects, Law of Demeterclass Ratings {  User user;    @Inject Ratings(User user) {      this.user = user;    }}
Fix: Partial objects, Law of Demeterclass Ratings {  User user;    // How do we inject this User like we need it?    // Wh...
Fix: Partial objects, Law of Demeter@ProvidesUser providesUser(AccountManager manager, Queue queue) {  User user = manager...
Fix: Partial objects, Law of Demeter@ProvidesUser providesUser(AccountManager manager, Queue queue) {  User user = manager...
Fix: Partial objects, Law of Demeter@ProvidesUser providesUser(AccountManager manager, Queue queue) {  User user = manager...
Fix: Partial objects, Law of Demeter@ProvidesUser providesUser(AccountManager manager, Queue queue) {  User user = manager...
Test: Partial objects, Law of DemeterRatings ratings; //SUT@Test void ratings_findAverage() {  ratings = new Ratings(new D...
Warning signs: Constructor does real workNew keyword in constructor or fieldsStatic method callsAnything more than field a...
Smell: Digging into Collaborators
Flavour: Digging aroundclass TicketPriceCalculator {  MovieTheaterService service;    TicketPriceCalculator(MovieTheaterSe...
Flavour: Digging aroundclass TicketPriceCalculator {  MovieTheaterService service;    TicketPriceCalculator(MovieTheaterSe...
Hard to test? Digging aroundTicketPriceCalculator calculator; //SUT@Mock MovieTheaterService service;@Test void calculateP...
Hard to test? Digging aroundTicketPriceCalculator calculator; //SUT@Mock MovieTheaterService service;@Test void calculateP...
Flavour: Digging aroundclass TicketPriceCalculator {  MovieTheaterService service;    TicketPriceCalculator(MovieTheaterSe...
Flavour: Digging aroundclass TicketPriceCalculator {  MovieTheaterService service;    TicketPriceCalculator(MovieTheaterSe...
Fix: Digging aroundclass TicketPriceCalculator {  MovieTheaterService service;    TicketPriceCalculator(MovieTheaterServic...
Fix: Digging aroundTicketPriceCalculator calculator; //SUT@Mock MovieTheaterService service;@Test void calculatePrice_tooM...
Digging into Collaborators,more flavours
Flavour: "Context", "Container"...class MovieTheaterService {    void processOrder(UserContext context) {      User user =...
Flavour: "Context", "Container"...class MovieTheaterService {    void processOrder(UserContext context) {      User user =...
Flavour: "Context", "Container"...class MovieTheaterService {    void processOrder(UserContext context) {      User user =...
Flavour: "Context", "Container"...class MovieTheaterService {    void processOrder(UserContext context) {      User user =...
Fix: "Context", "Container"...class MovieTheaterService {    void processOrder(User user, Order order) {      order.proces...
Warning signs: Digging into CollaboratorsObjects to use other objects, not themself.Having to create mocks that return oth...
Smell: Global state and Singletons
Flavour: They are liars!@Test void chargeCreditCard() { // Example: @mhevery  CreditCard cc = new CreditCard("9999 0000 77...
Flavour: They are liars!@Test void chargeCreditCard() {  CreditCard cc = new CreditCard("9999 0000 7777", 5, 2009);  asser...
Flavour: They are liars!@Test void chargeCreditCard() {  CreditCardProcessor.init();  CreditCard cc = new CreditCard("9999...
Flavour: They are liars!@Test void chargeCreditCard() {  CreditCardProcessor.init();  CreditCard cc = new CreditCard("9999...
Flavour: They are liars!@Test void chargeCreditCard() {  CreditCardProcessor.init();  OfflineQueue.start();  CreditCard cc...
Flavour: They are liars!@Test void chargeCreditCard() {  CreditCardProcessor.init();  OfflineQueue.start();  CreditCard cc...
Flavour: They are liars!@Test void chargeCreditCard() {  Database.connect("hsqldb");  CreditCardProcessor.init();  Offline...
Flavour: They are liars!@Test void chargeCreditCard() {  Database.connect("hsqldb"); // Global State  CreditCardProcessor....
How do you provide global variables in languageswithout global variables?"Dont. Your programs willthank you for taking the...
Fix: They are liars!
Fix: They are liars!Dependency Injection
Fix: They are liars!Dependency InjectionDependency Injection
Fix: They are liars!Dependency InjectionDependency InjectionDependency Injection
Fix: They are liars!Dependency InjectionDependency InjectionDependency InjectionDependency Injection
Fix: They are liars!@Test void chargeCreditCard() {  Database db = mock(Database.class);  Queue queue = new OfflineQueue(d...
Fix: They are liars!@Test void chargeCreditCard() {  Database db = mock(Database.class);  Queue queue = new OfflineQueue(d...
Fix: They are liars!@Test void chargeCreditCard() {  CreditCardProcessor ccProc = mock(CreditCardProcessor.class);  Credit...
About SingletonsSingletons and JVM SingletonsGang of four, static instance field.Best known as an Anti-pattern.But theres ...
About Global StateGlobal state is EvilDirty design, enables Action at a distance (anti-pattern),extremely-hard-to-test, et...
Warning signs: Global State and SingletonsUsing SingletonsStatic fields or static methods Dont confuse with helper or fact...
Smell: Class does too much
Smell: Class does too much, akaKitchen Sink
Smell: Class does too much, akaKitchen SinkDumping Ground
Smell: Class does too much, akaKitchen SinkDumping GroundThis Class does this AND that AND ...
Smell: Class does too much, akaKitchen SinkDumping GroundThis Class does this AND that AND ...God Class
Smell: Class does too much, akaKitchen SinkDumping GroundThis Class does this AND that AND ...God ClassQuijote
Smell: Class does too much, akaKitchen SinkDumping GroundThis Class does this AND that AND ...God ClassQuijote"You can loo...
Smell: Class does too muchinterface MovieTheater {    void open();    void addShowtime(Showtime showtime);    Set<Showtime...
Smell: Class does too muchinterface MovieTheater {    void open();    void addShowtime(Showtime showtime);    Set<Showtime...
Fix: Class does too muchinterface MovieTheater {  void open();    void addShowtime(Showtime showtime);    Set<Showtime> ge...
Warning signs: Class does too muchDescriptive names for this smellTiny scroll barMany fields, many methods Use Sonar and L...
Dependency Injection enablesyou to write testable code
Deep synergies betweentestable code and good design                     -- Michael Feathers
Maintainable code
Much more Guice!JSR330javax.inject -- Google + Spring effortRoboguiceAndroid, @InjectView, @InjectResourceDaggerFrom Squar...
ReferencesWriting testable code Miško HeveryWorking effectively with legacy code Michael FeathersThe testability Explorer ...
Thanks!slideshare.net/giro9    jordi@donky.org                                @jordi9
Q&A      jordi@donky.org              @jordi9
Upcoming SlideShare
Loading in...5
×

Clean code via dependency injection + guice

1,549

Published on

From Google DevFest 2012, Barcelona

Published in: Technology
0 Comments
14 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,549
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
63
Comments
0
Likes
14
Embeds 0
No embeds

No notes for slide

Clean code via dependency injection + guice

  1. 1. Clean code via Dependency Injection + Guice @jordi9bit.ly/devfest12cleancode 9 noviembre 2012
  2. 2. ... shameless self-promotion bro!
  3. 3. About youGang of Four?Legacy Code?Dependency Injection?Monkey patching?Monkey testing?Unit testing?TDD / BDD?Agile? XP? Scrum what?
  4. 4. S.O.L.I.D.Single responsibility principleOpen/closed principleLiskov substitution principleInterface segregation principleDependency inversion principle
  5. 5. Legacy code is code without test
  6. 6. F.I.R.S.T.Fast Run hundreds or thousands per secondIsolated Failure reasons become obviousReliable Run repeatedly in any order, any timeSelf-validating No manual evaluation requiredTimely Written before the code
  7. 7. Write some code, kill technical debt (or not)
  8. 8. Showtime!class Showtimes { MovieRepository repository; Showtimes() { repository = MovieRepositoryFactory.getInstance(); } Set<Movie> findMovies(City city) { return repository.findByCity(city); }}
  9. 9. Smell: Constructor does real workclass Showtimes { MovieRepository repository; Showtimes() { repository = MovieRepositoryFactory.getInstance(); } Set<Movie> findMovies(City city) { return repository.findByCity(city); }}
  10. 10. Smell: Constructor does real workclass Showtimes { MovieRepository repository; Showtimes() { repository = MovieRepositoryFactory.getInstance(); } Set<Movie> findMovies(City city) { return repository.findByCity(city); }}
  11. 11. Smell: Constructor does real workclass Showtimes { MovieRepository repository; Showtimes() { repository = MovieRepositoryFactory.getInstance(); } Set<Movie> findMovies(City city) { return repository.findByCity(city); }}
  12. 12. Factory Pattern, boilerplatepublic class MovieRepositoryFactory { static class MovieRepositoryHolder { static MovieRepository instance = new MovieRepositoryImpl(); } public static MovieRepository getInstance() { return MovieRepositoryHolder.instance; } static void setInstance(MovieRepository mock) { MovieRepositoryHolder.instance = mock; }}
  13. 13. Factory Pattern, boilerplatepublic class MovieRepositoryFactory { static class MovieRepositoryHolder { static MovieRepository instance = new MovieRepositoryImpl(); } public static MovieRepository getInstance() { return MovieRepositoryHolder.instance; } static void setInstance(MovieRepository mock) { MovieRepositoryHolder.instance = mock; }}
  14. 14. Factory Pattern, boilerplatepublic class MovieRepositoryFactory { static class MovieRepositoryHolder { static MovieRepository instance = new MovieRepositoryImpl(); } public static MovieRepository getInstance() { return MovieRepositoryHolder.instance; } static void setInstance(MovieRepository mock) { MovieRepositoryHolder.instance = mock; }}
  15. 15. Factory Pattern, boilerplatepublic class MovieRepositoryFactory { static class MovieRepositoryHolder { static MovieRepository instance = new MovieRepositoryImpl(); } public static MovieRepository getInstance() { return MovieRepositoryHolder.instance; } static void setInstance(MovieRepository mock) { MovieRepositoryHolder.instance = mock; }}
  16. 16. Factory Pattern, boilerplatepublic class MovieRepositoryFactory { static class MovieRepositoryHolder { static MovieRepository instance = new MovieRepositoryImpl(); } public static MovieRepository getInstance() { return MovieRepositoryHolder.instance; } static void setInstance(MovieRepository mock) { MovieRepositoryHolder.instance = mock; }}
  17. 17. Factory Pattern, boilerplatepublic class MovieRepositoryFactory { static class MovieRepositoryHolder { static MovieRepository instance = new MovieRepositoryImpl(); } public static MovieRepository getInstance() { return MovieRepositoryHolder.instance; } static void setInstance(MovieRepository mock) { MovieRepositoryHolder.instance = mock; }}
  18. 18. Hard to test@Testvoid findMovies_cantTestYouDude() { Showtimes showtimes = new Showtimes(); // FRAK! // No database setup... Im stuck with my collaborators // Cant really change it... Will it run in my // partners computer?}
  19. 19. Fix: Constructor does real work
  20. 20. Fix: Constructor does real workclass Showtimes { MovieRepository repository; Showtimes(MovieRepository repository) { this.repository = repository; } Set<Movie> findMovies(City city) { return repository.findByCity(city); }}
  21. 21. Fix: Constructor does real workclass Showtimes { // Hollywood principle: "Dont call us, well call you" MovieRepository repository; Showtimes(MovieRepository repository) { this.repository = repository; } Set<Movie> findMovies(City city) { return repository.findByCity(city); }}
  22. 22. Fix: Constructor does real workclass Showtimes { // Hollywood principle: "Dont call us, well call you" MovieRepository repository; @Inject Showtimes(MovieRepository repository) { this.repository = repository; } Set<Movie> findMovies(City city) { return repository.findByCity(city); }}
  23. 23. Fix: MovieRepository creation?class App { public static void main(String[] args) { // Manual Dependency Injection... but much more // boilerplate! // Maybe some <init> method... but thats why // constructors exist right? // Some container injection... mmm... Spring, Pico? // Or better, Guice time! }}
  24. 24. Fix: MovieRepository creationclass App { public static void main(String[] args) { Injector injector = Guice.createInjector( new AbstractModule() { @Override void configure() { bind(MovieRepository.class) .to(JpaMovieRepository.class); } }); injector.getInstance(Showtimes.class).findMovies(bcn); }}
  25. 25. Fix: MovieRepository creationclass App { public static void main(String[] args) { Injector injector = Guice.createInjector( new AbstractModule() { @Override void configure() { bind(MovieRepository.class) .to(JpaMovieRepository.class); } }); injector.getInstance(Showtimes.class).findMovies(bcn); }}
  26. 26. Fix: MovieRepository creationclass App { public static void main(String[] args) { Injector injector = Guice.createInjector( new AbstractModule() { @Override void configure() { bind(MovieRepository.class) .to(JpaMovieRepository.class); } }); // Injection is VIRAL! Avoid at all cost using the // Injector in more than one place (aka Service Locator) injector.getInstance(Showtimes.class).findMovies(bcn); }}
  27. 27. Fix: Testable and flexible designShowtimes showtimes;@Mock MovieRepository repository;@Test void findMovies_easyNow() { showtimes = new Showtimes(repository); given(repository.findByCity(bcn)) .willReturn(ImmutableSet.of(looper)); Set<Movie> movies = showtimes.findMovies(bcn); assertThat(movies, hasItem(looper));}
  28. 28. Fix: Testable and flexible designShowtimes showtimes; //SUT@Mock MovieRepository repository;@Test void findMovies_easyNow() { showtimes = new Showtimes(repository); given(repository.findByCity(bcn)) .willReturn(ImmutableSet.of(looper)); Set<Movie> movies = showtimes.findMovies(bcn); assertThat(movies, hasItem(looper));}
  29. 29. Fix: Testable and flexible designShowtimes showtimes; //SUT@Mock MovieRepository repository; // <3 Mockito, BDD@Test void findMovies_easyNow() { showtimes = new Showtimes(repository); //given given(repository.findByCity(bcn)) .willReturn(ImmutableSet.of(looper)); //when Set<Movie> movies = showtimes.findMovies(bcn); //then assertThat(movies, hasItem(looper));}
  30. 30. Fix: Testable and flexible designShowtimes showtimes; //SUT@Mock MovieRepository repository; // <3 Mockito, BDD@Test void findMovies_easyNow() { showtimes = new Showtimes(repository); //given given(repository.findByCity(bcn)) .willReturn(ImmutableSet.of(looper)); //when Set<Movie> movies = showtimes.findMovies(bcn); //then assertThat(movies, hasItem(looper)); // <3 Hamcrest}
  31. 31. Fix: Testable and flexible designShowtimes showtimes; //SUT@Mock MovieRepository repository; // <3 Mockito, BDD@Test void findMovies_easyNow() { showtimes = new Showtimes(repository); //given given(repository.findByCity(bcn)) .willReturn(ImmutableSet.of(looper)); // <3 Guava //when Set<Movie> movies = showtimes.findMovies(bcn); //then assertThat(movies, hasItem(looper)); // <3 Hamcrest}
  32. 32. Constructor does real work,more flavours
  33. 33. Flavour: Partial objects, Law of Demeterclass Ratings { User user; Ratings(AccountManager manager) { user = manager.getLoggedUser(); user.setQueue(new ItTakesForeverToBuildOSSQueue()); }}
  34. 34. Flavour: Partial objects, Law of Demeterclass Ratings { User user; Ratings(AccountManager manager) { user = manager.getLoggedUser(); user.setQueue(new ItTakesForeverToBuildOSSQueue()); } // Test? Slow, really slow // We dont use AccountManager anywhere else}
  35. 35. Fix: Partial objects, Law of Demeterclass Ratings { User user; @Inject Ratings(User user) { this.user = user; }}
  36. 36. Fix: Partial objects, Law of Demeterclass Ratings { User user; // How do we inject this User like we need it? // Wheres the Queue? @Inject Ratings(User user) { this.user = user; }}
  37. 37. Fix: Partial objects, Law of Demeter@ProvidesUser providesUser(AccountManager manager, Queue queue) { User user = manager.getLoggedUser(); user.setQueue(queue); return user;}@Provides @SingletonQueue providesLongRunningQueue() { return new ItTakesForeverToBuildOSSQueue();}
  38. 38. Fix: Partial objects, Law of Demeter@ProvidesUser providesUser(AccountManager manager, Queue queue) { User user = manager.getLoggedUser(); user.setQueue(queue); return user;}@Provides @SingletonQueue providesLongRunningQueue() { return new ItTakesForeverToBuildOSSQueue();}
  39. 39. Fix: Partial objects, Law of Demeter@ProvidesUser providesUser(AccountManager manager, Queue queue) { User user = manager.getLoggedUser(); user.setQueue(queue); return user;}@Provides @Singleton // no JVM Singleton, yay!Queue providesLongRunningQueue() { return new ItTakesForeverToBuildOSSQueue();}
  40. 40. Fix: Partial objects, Law of Demeter@ProvidesUser providesUser(AccountManager manager, Queue queue) { User user = manager.getLoggedUser(); user.setQueue(queue); return user;}@Provides @Singleton // no JVM Singleton, yay!Queue providesLongRunningQueue() { return new ItTakesForeverToBuildOSSQueue();}// Provider methods should be inside a Module
  41. 41. Test: Partial objects, Law of DemeterRatings ratings; //SUT@Test void ratings_findAverage() { ratings = new Ratings(new DummyUser()); // Easy to test now, we can have a mock or test-double // and check everything about Ratings. We dont care // about collaborators.}
  42. 42. Warning signs: Constructor does real workNew keyword in constructor or fieldsStatic method callsAnything more than field assignmentsObject not fully initialized after constructorControl flow
  43. 43. Smell: Digging into Collaborators
  44. 44. Flavour: Digging aroundclass TicketPriceCalculator { MovieTheaterService service; TicketPriceCalculator(MovieTheaterService service) { this.service = service; } BigDecimal calculatePrice(User user, Invoice invoice) { UserBalance balance = user.getBalance(); BigDecimal amount = invoice.getSubtotal(); return service.calculateTotal(balance, amount); }}
  45. 45. Flavour: Digging aroundclass TicketPriceCalculator { MovieTheaterService service; TicketPriceCalculator(MovieTheaterService service) { this.service = service; } BigDecimal calculatePrice(User user, Invoice invoice) { UserBalance balance = user.getBalance(); BigDecimal amount = invoice.getSubtotal(); return service.calculateTotal(balance, amount); }}
  46. 46. Hard to test? Digging aroundTicketPriceCalculator calculator; //SUT@Mock MovieTheaterService service;@Test void calculatePrice_tooMuchWork() { calculator = new TicketPriceCalculator(service); given(user.getBalance()).willReturn(new DummyBalance()); given(invoice.getSubtotal()).willReturn(new BigDecimal("9.3")); BigDecimal result = calculator.calculatePrice(balance, invoice); assertThat(result, is(new BigDecimal("8.3"));}
  47. 47. Hard to test? Digging aroundTicketPriceCalculator calculator; //SUT@Mock MovieTheaterService service;@Test void calculatePrice_tooMuchWork() { calculator = new TicketPriceCalculator(service); given(user.getBalance()).willReturn(new DummyBalance()); given(invoice.getSubtotal()).willReturn(new BigDecimal("9.3")); BigDecimal result = calculator.calculatePrice(balance, invoice); assertThat(result, is(new BigDecimal("8.3"));}
  48. 48. Flavour: Digging aroundclass TicketPriceCalculator { MovieTheaterService service; TicketPriceCalculator(MovieTheaterService service) { this.service = service; } // Deceitful API, Law of Demeter BigDecimal calculatePrice(User user, Invoice invoice) { UserBalance balance = user.getBalance(); BigDecimal amount = invoice.getSubtotal(); return service.calculateTotal(balance, amount); }}
  49. 49. Flavour: Digging aroundclass TicketPriceCalculator { MovieTheaterService service; TicketPriceCalculator(MovieTheaterService service) { this.service = service; } // Problem? Imagine this in a big application, your code // will be much more complicated than you (and your team) need BigDecimal calculatePrice(User user, Invoice invoice) { UserBalance balance = user.getBalance(); BigDecimal amount = invoice.getSubtotal(); return service.calculateTotal(balance, amount); }}
  50. 50. Fix: Digging aroundclass TicketPriceCalculator { MovieTheaterService service; TicketPriceCalculator(MovieTheaterService service) { this.service = service; } // Cleaner API, forget about "Middle men" class BigDecimal calculatePrice(UserBalance balance, BigDecimal am) { return service.calculateTotal(balance, am); }}
  51. 51. Fix: Digging aroundTicketPriceCalculator calculator; //SUT@Mock MovieTheaterService service;@Test void calculatePrice_tooMuchWork() { calculator = new TicketPriceCalculator(service); UserBalance balance = new DummyBalance("1.0"); BigDecimal amount = new BigDecimal("9.3")); BigDecimal result = calculator.calculatePrice(balance, amount); assertThat(result, is(new BigDecimal("8.3"));}
  52. 52. Digging into Collaborators,more flavours
  53. 53. Flavour: "Context", "Container"...class MovieTheaterService { void processOrder(UserContext context) { User user = context.getUser(); Order order = context.getOrders().byUser(user).now(); order.process(); }}
  54. 54. Flavour: "Context", "Container"...class MovieTheaterService { void processOrder(UserContext context) { User user = context.getUser(); Order order = context.getOrders().byUser(user).now(); order.process(); }}
  55. 55. Flavour: "Context", "Container"...class MovieTheaterService { void processOrder(UserContext context) { User user = context.getUser(); Order order = context.getOrders().byUser(user).now(); order.process(); } // Test? Create a UserContext with ton of mocks and stuff}
  56. 56. Flavour: "Context", "Container"...class MovieTheaterService { void processOrder(UserContext context) { User user = context.getUser(); Order order = context.getOrders().byUser(user).now(); order.process(); } // Test? Create a UserContext with ton of mocks and stuff}
  57. 57. Fix: "Context", "Container"...class MovieTheaterService { void processOrder(User user, Order order) { order.process(); } // Test? Sure! You got everything you need in place.}
  58. 58. Warning signs: Digging into CollaboratorsObjects to use other objects, not themself.Having to create mocks that return other mocksLaw of Demeter everywhereSeeing more than one period (.) in a methodchaining. Going deep in the object graph: mgs.getSolid().findOcelot().now()Exception: DSLsSuspicious names Context, Environment, Container...
  59. 59. Smell: Global state and Singletons
  60. 60. Flavour: They are liars!@Test void chargeCreditCard() { // Example: @mhevery CreditCard cc = new CreditCard("9999 0000 7777", 5, 2009); assertThat(cc.charge(30.0), is(true));}
  61. 61. Flavour: They are liars!@Test void chargeCreditCard() { CreditCard cc = new CreditCard("9999 0000 7777", 5, 2009); assertThat(cc.charge(30.0), is(true)); // Fails at runtime: // java.lang.NullPointerExpection}
  62. 62. Flavour: They are liars!@Test void chargeCreditCard() { CreditCardProcessor.init(); CreditCard cc = new CreditCard("9999 0000 7777", 5, 2009); assertThat(cc.charge(30.0), is(true));}
  63. 63. Flavour: They are liars!@Test void chargeCreditCard() { CreditCardProcessor.init(); CreditCard cc = new CreditCard("9999 0000 7777", 5, 2009); assertThat(cc.charge(30.0), is(true)); // Argh! This is getting awkward // java.lang.NullPointerExpection}
  64. 64. Flavour: They are liars!@Test void chargeCreditCard() { CreditCardProcessor.init(); OfflineQueue.start(); CreditCard cc = new CreditCard("9999 0000 7777", 5, 2009); assertThat(cc.charge(30.0), is(true));}
  65. 65. Flavour: They are liars!@Test void chargeCreditCard() { CreditCardProcessor.init(); OfflineQueue.start(); CreditCard cc = new CreditCard("9999 0000 7777", 5, 2009); assertThat(cc.charge(30.0), is(true)); // Are you kidding? // java.lang.NullPointerExpection}
  66. 66. Flavour: They are liars!@Test void chargeCreditCard() { Database.connect("hsqldb"); CreditCardProcessor.init(); OfflineQueue.start(); CreditCard cc = new CreditCard("9999 0000 7777", 5, 2009); assertThat(cc.charge(30.0), is(true));}
  67. 67. Flavour: They are liars!@Test void chargeCreditCard() { Database.connect("hsqldb"); // Global State CreditCardProcessor.init(); // Global State OfflineQueue.start(); // Global State CreditCard cc = new CreditCard("9999 0000 7777", 5, 2009); assertThat(cc.charge(30.0), is(true)); // How do we know this? Sure its documented somewhere... right?}
  68. 68. How do you provide global variables in languageswithout global variables?"Dont. Your programs willthank you for taking thetime to think about designinstead." -- Kent Beck, JUnit and TDD creator
  69. 69. Fix: They are liars!
  70. 70. Fix: They are liars!Dependency Injection
  71. 71. Fix: They are liars!Dependency InjectionDependency Injection
  72. 72. Fix: They are liars!Dependency InjectionDependency InjectionDependency Injection
  73. 73. Fix: They are liars!Dependency InjectionDependency InjectionDependency InjectionDependency Injection
  74. 74. Fix: They are liars!@Test void chargeCreditCard() { Database db = mock(Database.class); Queue queue = new OfflineQueue(db); CreditCardProcessor ccProc = new CreditCardProcessor(queue); CreditCard cc = new CreditCard(ccProc, "9999 0000 7777"); cc.charge(30.0);}
  75. 75. Fix: They are liars!@Test void chargeCreditCard() { Database db = mock(Database.class); Queue queue = new OfflineQueue(db); CreditCardProcessor ccProc = new CreditCardProcessor(queue); CreditCard cc = new CreditCard(ccProc, "9999 0000 7777"); cc.charge(30.0);}
  76. 76. Fix: They are liars!@Test void chargeCreditCard() { CreditCardProcessor ccProc = mock(CreditCardProcessor.class); CreditCard cc = new CreditCard(ccProc, "9999 0000 7777"); cc.charge(30.0);}
  77. 77. About SingletonsSingletons and JVM SingletonsGang of four, static instance field.Best known as an Anti-pattern.But theres nothing wrong about having oneinstance of an ObjectApplication SingletonManaged by someone, eg: Guice@Singleton scope
  78. 78. About Global StateGlobal state is EvilDirty design, enables Action at a distance (anti-pattern),extremely-hard-to-test, etc.Caveat: When its ok?Primitive or immutable constant reference static final String DEVFEST = "Devfest rocks"When information only travels one way org.slf4j.Logger // but hard to test
  79. 79. Warning signs: Global State and SingletonsUsing SingletonsStatic fields or static methods Dont confuse with helper or factory methodsStatic initialization block class App { static { MutableObject object = Registry.find(); } }Service Locators
  80. 80. Smell: Class does too much
  81. 81. Smell: Class does too much, akaKitchen Sink
  82. 82. Smell: Class does too much, akaKitchen SinkDumping Ground
  83. 83. Smell: Class does too much, akaKitchen SinkDumping GroundThis Class does this AND that AND ...
  84. 84. Smell: Class does too much, akaKitchen SinkDumping GroundThis Class does this AND that AND ...God Class
  85. 85. Smell: Class does too much, akaKitchen SinkDumping GroundThis Class does this AND that AND ...God ClassQuijote
  86. 86. Smell: Class does too much, akaKitchen SinkDumping GroundThis Class does this AND that AND ...God ClassQuijote"You can look at everything except this Class"
  87. 87. Smell: Class does too muchinterface MovieTheater { void open(); void addShowtime(Showtime showtime); Set<Showtime> getShowtimes(); void purchaseTicket(Buyer buyer, Movie movie); UserBalance findUserBalance(User balance); void processOrder(Order order); // ...}
  88. 88. Smell: Class does too muchinterface MovieTheater { void open(); void addShowtime(Showtime showtime); Set<Showtime> getShowtimes(); void purchaseTicket(Buyer buyer, Movie movie); UserBalance findUserBalance(User balance); void processOrder(Order order); // ...}
  89. 89. Fix: Class does too muchinterface MovieTheater { void open(); void addShowtime(Showtime showtime); Set<Showtime> getShowtimes();}interface BillingService { void purchaseTicket(Buyer buyer, Movie movie); UserBalance findUserBalance(User balance); void processOrder(Order order);}
  90. 90. Warning signs: Class does too muchDescriptive names for this smellTiny scroll barMany fields, many methods Use Sonar and LCOM4 (Lack of Cohesion) to check thisMany collaboratorsJust dont getting the Class
  91. 91. Dependency Injection enablesyou to write testable code
  92. 92. Deep synergies betweentestable code and good design -- Michael Feathers
  93. 93. Maintainable code
  94. 94. Much more Guice!JSR330javax.inject -- Google + Spring effortRoboguiceAndroid, @InjectView, @InjectResourceDaggerFrom Square, original Guice committers,built on JSR330, pretty exciting!
  95. 95. ReferencesWriting testable code Miško HeveryWorking effectively with legacy code Michael FeathersThe testability Explorer blog Blog, Miško HeveryDependency Injection Book, Dhanji R. PrasannaTest Driven - Practical TDD for Java devs Book, Lasse Koskela (2007)Effective Unit Testing - For Java devs Book, Lasse Koskela (2012)xUnit Test Patterns Book, Gerard MeszarosPainless Testing Slides, in Java or Python. Johannes Seitz
  96. 96. Thanks!slideshare.net/giro9 jordi@donky.org @jordi9
  97. 97. Q&A jordi@donky.org @jordi9
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×