Successfully reported this slideshow.
Your SlideShare is downloading. ×

Desiging for Modularity with Java 9

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Loading in …3
×

Check these out next

1 of 80 Ad

Desiging for Modularity with Java 9

Download to read offline

Java 9 brings modules as a core concept to the platform, but it’s more than just a language feature. With modules in Java 9, we can improve the design of code to increase maintainability and extensibility. As with every design principle, modularity requires thought and trade-offs to really reap the benefits. This session covers design practices for making codebases more maintainable and extensible. You will also find out about trade-offs to help you make the best choices. Topics include hiding implementations, using services for extensibility, API modules, avoiding cycles, optional dependencies, and dynamically loading modules. Familiarity with modules is helpful but not required. The speakers are the authors of Java 9 Modularity (O’Reilly).

Also see https://javamodularity.com

Java 9 brings modules as a core concept to the platform, but it’s more than just a language feature. With modules in Java 9, we can improve the design of code to increase maintainability and extensibility. As with every design principle, modularity requires thought and trade-offs to really reap the benefits. This session covers design practices for making codebases more maintainable and extensible. You will also find out about trade-offs to help you make the best choices. Topics include hiding implementations, using services for extensibility, API modules, avoiding cycles, optional dependencies, and dynamically loading modules. Familiarity with modules is helpful but not required. The speakers are the authors of Java 9 Modularity (O’Reilly).

Also see https://javamodularity.com

Advertisement
Advertisement

More Related Content

Slideshows for you (20)

Similar to Desiging for Modularity with Java 9 (20)

Advertisement

More from Sander Mak (@Sander_Mak) (20)

Recently uploaded (20)

Advertisement

Desiging for Modularity with Java 9

  1. 1. Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9
  2. 2. Today's journey Module primer Services & DI Modular design Layers & loading
  3. 3. Designing for Modularity with Java 9 What if we ... ... forget about the classpath ... embrace modules ... want to create truly modular software?
  4. 4. Designing for Modularity with Java 9 What if we ... ... forget about the classpath ... embrace modules ... want to create truly modular software? Design Constraints Patterns
  5. 5. A Module Primer module easytext.cli { requires easytext.analysis; } module easytext.analysis { exports analysis.api; opens impl; } easytext.cli easytext.analysis other analysis.api impl reflection only!
  6. 6. A Module Primer module easytext.cli { requires easytext.analysis; } module easytext.analysis { exports analysis.api; opens impl; } Modules define dependencies explicitly easytext.cli easytext.analysis other analysis.api impl reflection only!
  7. 7. module easytext.cli { requires easytext.analysis; } A Module Primer module easytext.analysis { exports analysis.api; opens impl; } Packages are encapsulated by default easytext.cli easytext.analysis other analysis.api impl reflection only!
  8. 8. module easytext.cli { requires easytext.analysis; } A Module Primer module easytext.analysis { exports analysis.api; opens impl; } Packages can be “opened” for deep reflection at run-time easytext.cli easytext.analysis other analysis.api impl reflection only!
  9. 9. Services
  10. 10. EasyText example easytext.gui easytext.analysis.kincaid easytext.analysis.coleman easytext.cli analysis.api
  11. 11. EasyText example easytext.gui easytext.analysis.kincaid easytext.analysis.coleman easytext.cli analysis.api
  12. 12. Encapsulation vs. decoupling Analyzer i = new KincaidAnalyzer(); ‣ Even with interfaces, an instance has to be created…
  13. 13. Encapsulation vs. decoupling Analyzer i = new KincaidAnalyzer(); ‣ Even with interfaces, an instance has to be created… ‣ We need to export our implementation! :-( module easytext.kincaid { exports easytext.analysis.kincaid; }
  14. 14. Reflection isn’t a workaround Class myImpl = Class.forName(…); MyInterface i = myImpl.newInstance(); ‣ Package needs to be open module easytext.kincaid { opens easytext.analysis.kincaid; }
  15. 15. Services to the rescue easytext.gui easytext.analysis.kincaid easytext.analysis.coleman easytext.cli Module System uses uses providesprovides
  16. 16. Providing a service module easytext.analyzer.kincaid { requires easytext.analysis.api; provides javamodularity.easytext.analysis.api.Analyzer with javamodularity.easytext.analysis.kincaid.KincaidAnalyzer; }
  17. 17. Implementing a service public class KincaidAnalyzer implements Analyzer { public KincaidAnalyzer() { } @Override public double analyze(List<List<String>> sentences) { … } } ‣ Just a plain Java class ‣ Not exported!
  18. 18. Consuming a service module easytext.cli { requires easytext.analysis.api; uses javamodularity.easytext.analysis.api.Analyzer; }
  19. 19. Consuming a service Iterable<Analyzer> analyzers = ServiceLoader.load(Analyzer.class); for (Analyzer analyzer: analyzers) { System.out.println( analyzer.getName() + ": " + analyzer.analyze(sentences)); } module easytext.cli { requires easytext.analysis.api; uses javamodularity.easytext.analysis.api.Analyzer; }
  20. 20. Finding the right service ServiceLoader<Analyzer> analyzers = ServiceLoader.load(Analyzer.class); analyzers.stream() .filter(provider -> …) .map(ServiceLoader.Provider::get) .forEach(analyzer -> System.out.println(analyzer.getName())); ‣ ServiceLoader supports streams ‣ Lazy instantiation of services
  21. 21. Add new analyses by adding provider modules on the module path
  22. 22. Dependency Injection
  23. 23. Providing a Guice service public class ColemanModule extends AbstractModule { @Override protected void configure() { Multibinder .newSetBinder(binder(), Analyzer.class) .addBinding().to(ColemanAnalyzer.class); } ‣ Provider module should export a Guice Module
  24. 24. Providing a Guice service ‣ Consumers must be able to compile against the Guice module ‣ Service impl package must be open module easytext.algorithm.coleman { requires easytext.algorithm.api; requires guice; requires guice.multibindings; exports javamodularity.easytext.algorithm.coleman.guice; opens javamodularity.easytext.algorithm.coleman; } javamodularity │   └── easytext │   └── algorithm │   └── coleman │   ├── ColemanAnalyzer.java │   └── guice │   └── ColemanModule.java └── module-info.java
  25. 25. Consuming a Guice service public class Main { public static void main(String... args) { Injector injector = Guice.createInjector( new ColemanModule(), new KincaidModule()); CLI cli = injector.getInstance(CLI.class); cli.analyze(args[0]); } }
  26. 26. Consuming a Guice service ‣ Require the implementation Guice modules ‣ Open package for Guice injection module easytext.cli { requires easytext.algorithm.api; requires guice; requires easytext.algorithm.coleman; requires easytext.algorithm.kincaid; opens javamodularity.easytext.cli; }
  27. 27. Consuming a Guice service ‣ Now we can @Inject public class CLI { private final Set<Analyzer> analyzers; @Inject public CLI(Set<Analyzer> analyzers) { this.analyzers = analyzers; } …
  28. 28. Injecting a service algorithm.coleman guice Create binding
  29. 29. Injecting a service cli Requires Guice module algorithm.coleman guice Create binding
  30. 30. Injecting a service cli Requires Guice module algorithm.coleman guice Create binding Create injector
  31. 31. Injecting a service cli Requires Guice module algorithm.coleman guice Create binding Inject instance Create injector
  32. 32. Services vs Guice ‣ @Inject vs using an API ‣ Developers might be more familiar the model ‣ Requires more coupling ‣ Adding an implementation requires code changes Benefits of using Guice Downsides of using Guice
  33. 33. Services *and* Guice module easytext.algorithm.coleman { requires easytext.algorithm.api; requires guice; requires guice.multibindings; exports javamodularity.easytext.algorithm.coleman.guice; opens javamodularity.easytext.algorithm.coleman; }
  34. 34. Services *and* Guice module easytext.algorithm.coleman { requires easytext.algorithm.api; requires guice; requires guice.multibindings; exports javamodularity.easytext.algorithm.coleman.guice; opens javamodularity.easytext.algorithm.coleman; } provides com.google.inject.AbstractModule with javamodularity.easytext.algorithm.coleman.guice.ColemanModule
  35. 35. Services *and* Guice module easytext.cli { requires easytext.algorithm.api; requires guice; requires easytext.algorithm.coleman; requires easytext.algorithm.kincaid; opens javamodularity.easytext.cli; }
  36. 36. Services *and* Guice module easytext.cli { requires easytext.algorithm.api; requires guice; requires easytext.algorithm.coleman; requires easytext.algorithm.kincaid; opens javamodularity.easytext.cli; } uses com.google.inject.AbstractModule
  37. 37. Modular Design
  38. 38. Naming Modules ... is hard
  39. 39. Naming Modules ... is hard Application modules ‣ Short & memorable ‣ <application>.<component> ‣ e.g. easytext.gui
  40. 40. Naming Modules ... is hard Application modules ‣ Short & memorable ‣ <application>.<component> ‣ e.g. easytext.gui vs. Library modules ‣ Uniqueness ‣ Reverse DNS ‣ com.mycompany.ourlib ‣ 'Root package'
  41. 41. API Modules
  42. 42. API Modules ‣ Module with exported API only ‣ When multiple implementations are expected ‣ Can also contain 'default' implementation
  43. 43. API Modules ‣ Module with exported API only ‣ When multiple implementations are expected ‣ Can also contain 'default' implementation ‣ API modules export: ‣ Interfaces ‣ (Abstract) classes ‣ Exceptions ‣ Etc.
  44. 44. Small Modules vs. All-in-one
  45. 45. Small Modules vs. All-in-one ‣ Composability ‣ Reusability Small modules
  46. 46. Small Modules vs. All-in-one ‣ Composability ‣ Reusability Small modules ‣ Ease-of-use All-in-one modules ? ? ? ? ?
  47. 47. Small Modules vs. All-in-one Why choose? Use aggregator modules
  48. 48. Small Modules vs. All-in-one Why choose? Use aggregator modules module mylib { requires transitive mylib.one; requires transitive mylib.two; requires transitive mylib.three; }
 Module without code, using implied readability:
  49. 49. Small Modules vs. All-in-one Why choose? Use aggregator modules module mylib { requires transitive mylib.one; requires transitive mylib.two; requires transitive mylib.three; }
 Module without code, using implied readability: mylib mylib.one mylib.two mylib.three
  50. 50. Small Modules vs. All-in-one Why choose? Use aggregator modules mylib mylib.one mylib.two mylib.three application
  51. 51. Small Modules vs. All-in-one Why choose? Use aggregator modules mylib mylib.one mylib.two mylib.three application
  52. 52. Small Modules vs. All-in-one Why choose? Use aggregator modules mylib mylib.one mylib.two mylib.three application Implied readability
  53. 53. Small Modules vs. All-in-one mylib.extra mylib.core
  54. 54. Small Modules vs. All-in-one mylib.extra mylib.core application
  55. 55. Small Modules vs. All-in-one mylib.extra mylib.core application
  56. 56. Small Modules vs. All-in-one mylib.extra mylib.core application
  57. 57. Breaking cycles ‣ Non-modular JARs can have cyclic dependencies ‣ Modules can not
  58. 58. Breaking cycles
  59. 59. Breaking cycles public class Author { private String name; private List<Book> books; public Author(String name) { this.name = name; } // .. }
  60. 60. Breaking cycles public class Author { private String name; private List<Book> books; public Author(String name) { this.name = name; } // .. } public class Book { public Author author; public String title; public void printBook() { System.out.printf( "%s, by %snn%s", title, author.getName(), text); }
  61. 61. Breaking cycles public class Author { private String name; private List<Book> books; public Author(String name) { this.name = name; } // .. } public class Book { public Author author; public String title; public void printBook() { System.out.printf( "%s, by %snn%s", title, author.getName(), text); }
  62. 62. Breaking cycles public class Author { private String name; private List<Book> books; public Author(String name) { this.name = name; } // .. } public class Book { public Author author; public String title; public void printBook() { System.out.printf( "%s, by %snn%s", title, author.getName(), text); }
  63. 63. Breaking cycles: abstraction
  64. 64. Breaking cycles: abstraction public class Author implements Named { // .. public String getName() { return this.name; } }
  65. 65. Breaking cycles: abstraction public class Author implements Named { // .. public String getName() { return this.name; } } public interface Named { String getName(); }
  66. 66. Optional dependencies ‣ Requires means compile-time and run-time dependency ‣ What if we want an optional dependency? ‣ Use it if available at run-time ‣ Otherwise, run without
  67. 67. Optional dependencies ‣ Requires means compile-time and run-time dependency ‣ What if we want an optional dependency? ‣ Use it if available at run-time ‣ Otherwise, run without Compile-time only dependencies: module framework { requires static fastjsonlib; }
  68. 68. Optional dependencies ‣ Requires means compile-time and run-time dependency ‣ What if we want an optional dependency? ‣ Use it if available at run-time ‣ Otherwise, run without Compile-time only dependencies: module framework { requires static fastjsonlib; } Resolve fastjsonlib if available at run-time, ignore if not
  69. 69. Optional dependencies Avoid run-time exceptions!
  70. 70. Optional dependencies try { Class<?> clazz = Class.forName("javamodularity.fastjsonlib.FastJson"); FastJson instance = (FastJson) clazz.getConstructor().newInstance(); System.out.println("Using FastJson"); } catch (ReflectiveOperationException e) { System.out.println("Oops, we need a fallback!"); } Avoid run-time exceptions!
  71. 71. Optional dependencies Better yet: use services for optional dependencies module framework { requires json.api; uses json.api.Json; } ServiceLoader.load(Json.class) .stream() .filter(this::isFast) .findFirst();
  72. 72. Layers and dynamic module loading
  73. 73. ModuleLayer mods ├── easytext.algorithm.api ├── easytext.algorithm.coleman ├── easytext.algorithm.kincaid ├── easytext.algorithm.naivesyllablecounter ├── easytext.algorithm.nextgensyllablecounter ├── easytext.cli └── easytext.gui Module path
  74. 74. ModuleLayer mods ├── easytext.algorithm.api ├── easytext.algorithm.coleman ├── easytext.algorithm.kincaid ├── easytext.algorithm.naivesyllablecounter ├── easytext.algorithm.nextgensyllablecounter ├── easytext.cli └── easytext.gui Module path boot layer api coleman kincaid gui ... at run-time
  75. 75. ModuleLayer Immutable boot layer api coleman kincaid gui ... One version of a module No dynamic loading? App & platform modules
  76. 76. ModuleLayer boot layer api gui ... Create layers at run-time Multiple versions in layers Layers form hierarchy
  77. 77. ModuleLayer boot layer api gui ... layer 1 coleman parent Create layers at run-time Multiple versions in layers Layers form hierarchy
  78. 78. ModuleLayer boot layer api gui ... layer 1 coleman parent layer 2 kincaid syllable. counter parent Create layers at run-time Multiple versions in layers Layers form hierarchy
  79. 79. ModuleLayer Demo
  80. 80. Thank you. https://javamodularity.com Paul Bakker @pbakker Sander Mak @Sander_Mak

×