↗️ This README only covers one section of the demo. The master branch contains more information.
Now it's time to really get to know Jigsaw and split that monolith up into separate modules.
The "surprise API", i.e. Surprise
and SurpriseFactory
, is a great success and we want to separate it from the monolith.
Then there are the factories that create the surprises, which turn out to be very dynamic. A lot of work is being done here, they change frequently and which factories are used changes from release to release. So we want to isolate them.
At the same time we plan to create a large Christmas application of which the calendar is only one part. So we'd like to have a separate module for that as well.
We end up with these modules:
- surprise -
Surprise
andSurpriseFactory
- calendar - the calendar, which uses the surprise API
- factories - the
SurpriseFactory
implementations - advent - the original application, now hollowed out to the
Main
class
Looking at their dependencies we see that surprise depends on no other module of ours. Both calendar and factories make use of its types so they must depend on it. Finally, main uses the factories to create the calendar so it depends on both.
The first step is to reorganize the source code:
src - org.codefx.demo.advent.calendar: the "calendar" module - org ... module-info.java - org.codefx.demo.advent.factories: the "factories" module - org ... module-info.java - org.codefx.demo.advent.surprise: the "surprise" module - org ... module-info.java - org.codefx.demo.advent: the "main" module - org ... module-info.java .gitignore compileAndRun.sh LICENSE README
This is the same directory structure as used by the official quick start guide. The depcition is not complete, though, and does not contain the folders below org
, which are the individual packages and eventually the source files.
Let's start with surprise.
There are no required
clauses as it has no dependencies. (Except for java.base
, which is always implicitly required.) It exports the package org.codefx.demo.advent.surprise
because that contains the two classes Surprise
and SurpriseFactory
.
So the module-info.java
looks as follows:
module org.codefx.demo.advent.surprise { // requires no other modules // publicly accessible packages exports org.codefx.demo.advent.surprise; }
Compiling and packaging is very similar to the previous section. It is in fact even easier because surprises contains no main class:
# compile javac -d classes/org.codefx.demo.advent.surprise ${list of source files} # package jar -c --file=mods/org.codefx.demo.advent.surprise.jar ${compiled class files}
The calendar has fields and parameters with types from the surprise API so the module must depend on surprises. Adding requires org.codefx.demo.advent.surprise
to the module achieves this.
But there is an additional twist: The publicly accessible method Calendar::createWithSurprises
declares a parameter of type List<SurpriseFactory>
. So all modules using this API must also read surprises. Otherwise Jigsaw would prevent them from accessing these types, which would lead to compile and runtime errors. Marking the requires
clause as public
fixes this. With it any module that depends on calendar can also access surprises (called implied readability).
This module's API consists of the class Calendar
. For it to be publicly accessible the containing package org.codefx.demo.advent.calendar
must be exported. Note that CalendarSheet
, private to the same package, will not be visible outside the module. This is analog to before where a package-private class from another package was also not visible.
The final module-info looks as follows:
module org.codefx.demo.advent.calendar { // required modules requires public org.codefx.demo.advent.surprise; // publicly accessible packages exports org.codefx.demo.advent.calendar; }
Compilation is almost like before but the dependency on surprises must of course be reflected here. For that it suffices to point the compiler to the directory mods
as it contains the required module (the sum of such directories is called the module path):
# compile javac -mp mods -d classes/org.codefx.demo.advent.calendar ${list of source files} # package jar -c --file=mods/org.codefx.demo.advent.calendar.jar ${compiled class files}
The factories implement SurpriseFactory
so this module must obviously depend on theirs. And since they return instances of Surprise
from published methods the same line of though as above leads to a requires public
clause.
The factories can be found in the package org.codefx.demo.advent.factories
so that must be exported. Note that the public class AbstractSurpriseFactory
, which is found in another package, is not accessible outside this module. As it is currently implemented Jigsaw will also not allow reflection to access it. The only way around this are command line flags.
Together:
module org.codefx.demo.advent.factories { // required modules requires public org.codefx.demo.advent.surprise; // publicly accessible packages exports org.codefx.demo.advent.factories; }
Compilation and packaging is analog to calendar.
Our application requires the two modules calendar and factories to compile and run. It has no API to export.
module org.codefx.demo.advent { // required modules requires org.codefx.demo.advent.calendar; requires org.codefx.demo.advent.factories; // no exports }
Compiling and packaging is like with last section's single module except that the compiler needs to know where to look for required modules:
javac -mp mods -d classes/org.codefx.demo.advent ${list of source files} jar -c \ --file=mods/org.codefx.demo.advent.jar \ --main-class=org.codefx.demo.advent.Main \ ${compiled class files} java -mp mods -m org.codefx.demo.advent