Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander...

80
Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Transcript of Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander...

Page 1: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Designing for Modularity

Paul Bakker @pbakker

Sander Mak @Sander_Mak

with Java 9

Page 2: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Today's journey

Module primer

Services & DI

Modular design

Layers & loading

Page 3: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Designing for Modularity with Java 9

What if we ...

... forget about the classpath

... embrace modules

... want to create truly modular software?

Page 4: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Designing for Modularity with Java 9

What if we ...

... forget about the classpath

... embrace modules

... want to create truly modular software?

Design Constraints Patterns

Page 5: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

A Module Primer module easytext.cli { requires easytext.analysis;

}

module easytext.analysis { exports analysis.api; opens impl;

}

easytext.cli easytext.analysis

other

analysis.api

implreflection only!

Page 6: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

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

implreflection only!

Page 7: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

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

implreflection only!

Page 8: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

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

implreflection only!

Page 9: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Services

Page 10: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

EasyText example

easytext.gui

easytext.analysis.kincaid easytext.analysis.coleman

easytext.cli

analysis.api

Page 11: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

EasyText example

easytext.gui

easytext.analysis.kincaid easytext.analysis.coleman

easytext.cli

analysis.api

Page 12: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Encapsulation vs. decoupling

Analyzer i = new KincaidAnalyzer();

‣ Even with interfaces, an instance has to be created…

Page 13: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

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; }

Page 14: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

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; }

Page 15: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Services to the rescue

easytext.gui

easytext.analysis.kincaid easytext.analysis.coleman

easytext.cli

Module System

uses uses

providesprovides

Page 16: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Providing a service

module easytext.analyzer.kincaid { requires easytext.analysis.api; provides javamodularity.easytext.analysis.api.Analyzer

with javamodularity.easytext.analysis.kincaid.KincaidAnalyzer; }

Page 17: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

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!

Page 18: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Consuming a servicemodule easytext.cli { requires easytext.analysis.api;

uses javamodularity.easytext.analysis.api.Analyzer; }

Page 19: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

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; }

Page 20: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

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

Page 21: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Add new analyses by adding provider modules on the module path

Page 22: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Dependency Injection

Page 23: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

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

Page 24: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

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

Page 25: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Consuming a Guice servicepublic 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]); } }

Page 26: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

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; }

Page 27: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Consuming a Guice service

‣ Now we can @Injectpublic class CLI {

private final Set<Analyzer> analyzers;

@Inject public CLI(Set<Analyzer> analyzers) { this.analyzers = analyzers; } …

Page 28: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Injecting a service

algorithm.coleman

guice

Create binding

Page 29: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Injecting a service

cliRequires Guice module

algorithm.coleman

guice

Create binding

Page 30: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Injecting a service

cliRequires Guice module

algorithm.coleman

guice

Create binding

Create injector

Page 31: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Injecting a service

cliRequires Guice module

algorithm.coleman

guice

Create binding Inject instance

Create injector

Page 32: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

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

Page 33: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Services *and* Guicemodule easytext.algorithm.coleman { requires easytext.algorithm.api; requires guice; requires guice.multibindings; exports javamodularity.easytext.algorithm.coleman.guice; opens javamodularity.easytext.algorithm.coleman; }

Page 34: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Services *and* Guicemodule 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

Page 35: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Services *and* Guice

module easytext.cli { requires easytext.algorithm.api; requires guice; requires easytext.algorithm.coleman; requires easytext.algorithm.kincaid;

opens javamodularity.easytext.cli; }

Page 36: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

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

Page 37: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Modular Design

Page 38: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Naming Modules... is hard

Page 39: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Naming Modules... is hard

Application modules

‣ Short & memorable ‣ <application>.<component> ‣ e.g. easytext.gui

Page 40: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

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'

Page 41: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

API Modules

Page 42: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

API Modules‣ Module with exported API only ‣ When multiple implementations are expected ‣ Can also contain 'default' implementation

Page 43: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

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.

Page 44: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Small Modules vs. All-in-one

Page 45: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Small Modules vs. All-in-one

‣ Composability ‣ Reusability

Small modules

Page 46: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Small Modules vs. All-in-one

‣ Composability ‣ Reusability

Small modules

‣ Ease-of-use

All-in-one modules ??

?

??

Page 47: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Small Modules vs. All-in-oneWhy choose? Use aggregator modules

Page 48: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Small Modules vs. All-in-oneWhy choose? Use aggregator modules

module mylib { requires transitive mylib.one; requires transitive mylib.two; requires transitive mylib.three; }

Module without code, using implied readability:

Page 49: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Small Modules vs. All-in-oneWhy 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

Page 50: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Small Modules vs. All-in-oneWhy choose? Use aggregator modules

mylib

mylib.one

mylib.two

mylib.threeapplication

Page 51: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Small Modules vs. All-in-oneWhy choose? Use aggregator modules

mylib

mylib.one

mylib.two

mylib.threeapplication

Page 52: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Small Modules vs. All-in-oneWhy choose? Use aggregator modules

mylib

mylib.one

mylib.two

mylib.threeapplication

Implied readability

Page 53: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Small Modules vs. All-in-one

mylib.extra

mylib.core

Page 54: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Small Modules vs. All-in-one

mylib.extra

mylib.core

application

Page 55: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Small Modules vs. All-in-one

mylib.extra

mylib.core

application

Page 56: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Small Modules vs. All-in-one

mylib.extra

mylib.core

application

Page 57: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Breaking cycles‣ Non-modular JARs can have cyclic dependencies ‣ Modules can not

Page 58: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Breaking cycles

Page 59: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Breaking cycles

public class Author { private String name; private List<Book> books;

public Author(String name) { this.name = name; } // .. }

Page 60: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

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 %s\n\n%s", title, author.getName(), text); }

Page 61: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

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 %s\n\n%s", title, author.getName(), text); }

Page 62: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

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 %s\n\n%s", title, author.getName(), text); }

Page 63: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Breaking cycles: abstraction

Page 64: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Breaking cycles: abstraction

public class Author implements Named { // ..

public String getName() { return this.name; }

}

Page 65: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Breaking cycles: abstraction

public class Author implements Named { // ..

public String getName() { return this.name; }

}

public interface Named {

String getName();

}

Page 66: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

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

Page 67: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

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; }

Page 68: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

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

Page 69: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Optional dependenciesAvoid run-time exceptions!

Page 70: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

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!

Page 71: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Optional dependenciesBetter yet: use services for optional dependencies

module framework { requires json.api;

uses json.api.Json; }

ServiceLoader.load(Json.class) .stream() .filter(this::isFast) .findFirst();

Page 72: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Layers and dynamic module

loading

Page 73: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

ModuleLayermods ├── easytext.algorithm.api ├── easytext.algorithm.coleman ├── easytext.algorithm.kincaid ├── easytext.algorithm.naivesyllablecounter ├── easytext.algorithm.nextgensyllablecounter ├── easytext.cli └── easytext.gui

Module path

Page 74: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

ModuleLayermods ├── 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

Page 75: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

ModuleLayer

Immutable boot layer

api coleman

kincaid gui

...One version of a module

No dynamic loading?

App & platform modules

Page 76: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

ModuleLayer

boot layer

api

gui

...

Create layers at run-time

Multiple versions in layers

Layers form hierarchy

Page 77: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

ModuleLayer

boot layer

api

gui

...

layer 1

coleman

parent

Create layers at run-time

Multiple versions in layers

Layers form hierarchy

Page 78: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

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

Page 79: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

ModuleLayer Demo

Page 80: Designing for Modularity with Java 9€¦ · Designing for Modularity Paul Bakker @pbakker Sander Mak @Sander_Mak with Java 9

Thank you.

https://javamodularity.com

Paul Bakker @pbakker

Sander Mak @Sander_Mak