В Java 9 был включён проект Jigsaw, позволяющий создавать модульные проекты. В дополнение к пакетам модули позволяют лучше разделить ваш проект на различные части и поддерживать зависимости между ними в консистентном состоянии.
Цель
Изучить Jigsaw, научиться разбивать проект на модули и использовать их в своём приложении. Научиться использовать ServiceLoader.
Описание проекта
Я уже изучил JShell и неизменяемые коллекции в Java 9. Пришло время изучать одно из самых крупных изменений — модули Jigsaw. Изучать лучше на практике. Будет написан калькулятор с плагинами. Калькулятор будет представлять собой отдельный модуль, который экспортирует наружу интерфейс для расширения возможностей калькулятора. Другие модули могут поставлять реализации этого интерфейса, а основной модуль при старте проверяет все зарегистрированные реализации и добавляет кнопки с соответствующими действиями на панель.
Текущий статус
Создал основную форму с тремя основными группами элементов:
- Экран калькулятора.
- Цифровые кнопки.
- Возможность добавлять операции калькулятора как плагины.
- Модуль стандартных плагинов, который на текущий момент реализует только одну операцию суммирования операндов.
Общее описание реализации
Экранные формы написаны с использованием Swing. Интерфейс для реализации расширений плагинами содержит два метод:
1 2 3 4 5 6 |
package ru.urvanov.calculatorex.plugin; public interface Operation { String getButtonText(); double calculate(double x, double y); } |
Это не слишком удачный вариант реализации, так как он сильно ограничивает количество возможностей плагинов. В следующей версии, возможно, стоит передавать в метод calculate некое состояние калькулятора, а не просто два числа.
Файл описания основного модуля “module-info.java” экспортирует наш интерфейс Operation:
1 2 3 4 5 |
module ru.urvanov.calculatorex { requires java.desktop; exports ru.urvanov.calculatorex.plugin; uses ru.urvanov.calculatorex.plugin.Operation; } |
Модуль со стандартными плагинами пока содержит только одну добавленную операцию Sum:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package ru.urvanov.calculatorex.plugin.standard; import ru.urvanov.calculatorex.plugin.Operation; public class Sum implements Operation { public String getButtonText() { return "+"; } public double calculate(double x, double y) { return x + y; } } |
В описании “module-info.java” он указывает, что он поставляет эту реализацию:
1 2 3 4 5 |
module ru.urvanov.calculatorex.standardplugin { requires ru.urvanov.calculatorex; provides ru.urvanov.calculatorex.plugin.Operation with ru.urvanov.calculatorex.plugin.standard.Sum; } |
Основной модуль при старте проверяет наличие реализаций нашего интерфейса и добавляет соответствующие кнопки:
1 2 3 4 5 6 7 8 9 10 11 12 |
ServiceLoader<Operation> sl = ServiceLoader.load(Operation.class); System.out.println("before stream " + sl); StreamSupport.stream(sl.spliterator(), false).forEach(op -> { System.out.println("found " + op.getButtonText()); OperationButton btn = new OperationButton(op); btn.addActionListener(event -> { double y = readValue(); this.x = operation.calculate(x, y); this.operation = ((OperationButton) event.getSource()).getOperation(); }); CalculatorFrame.this.add(btn); }); |
Весь код выложил в репозиторий на GitHub и пометил меткой step1, чтобы было видно, что было на текущем этапе.