В этом блоге я иллюстрирую реализацию шаблона команд в стиле функционального программирования с использованием лямбда-выражений Java 8 . Назначение шаблона команды состоит в том, чтобы инкапсулировать запрос в виде объекта, тем самым параметризируя клиентов различными запросами, запросами очереди или журнала и поддерживая соответствующие операции. Шаблон команды — это способ написания универсального кода, который упорядочивает и выполняет методы на основе решений во время выполнения. Участники этого шаблона следующие:
- Команда — объявляет интерфейс для выполнения операции.
- ConcreteCommand — определяет привязку между объектом Receiver и действием.
- Клиент — создает экземпляр ConcreteCommand и устанавливает его получателя.
- Invoker — Управляет командой (-ами) для выполнения запроса (-ов).
- Приемник — выполняет фактическую работу.
Отношения между этими участниками изображены ниже:
Давайте рассмотрим конкретный пример шаблона команды и посмотрим, как он преобразуется с помощью лямбда-выражений. Предположим, у нас есть утилита файловой системы, которая выполняет действия, которые мы будем вызывать, такие как открытие файла, запись в файл и закрытие файла. Это может быть реализовано в виде макрофункции — то есть серии операций, которые можно записать, а затем запустить позже как одну операцию. Это будет наш приемник.
1
2
3
4
5
|
public interface FileSystemReceiver { void openFile(); void writeFile(); void closeFile(); } |
Каждая из операций, таких как openFile и writefile , является командами. Мы можем создать общий командный интерфейс, чтобы соответствовать этим различным операциям. Давайте назовем этот интерфейс Action, так как он представляет выполнение одного действия в нашем домене. Это интерфейс, который реализуют все наши объекты команд.
1
2
3
|
public interface Action { public void perform(); } |
Давайте теперь реализуем наш интерфейс Action для каждой из операций. Все эти классы должны сделать, это вызвать единственный метод на FileReceiver и обернуть этот вызов в наш интерфейс Action. Давайте назовем классы после операций, которые они переносят, с соответствующим соглашением об именах классов, поэтому метод openFile соответствует классу OpenFile .
01
02
03
04
05
06
07
08
09
10
11
12
13
|
public class OpenFile implements Action { private final FileReceiver fileReceiver; public OpenFile(FileReceiver fileReceiver) { this .fileReceiver = fileReceiver; } public void perform() { fileReceiver.openFile(); } } |
Теперь давайте реализуем наш класс Macro . Макрос состоит из последовательности действий, которые могут быть вызваны по очереди, и это будет действовать как вызывающая сторона. Этот класс может записывать действия и запускать их вместе. Мы можем сохранить последовательность действий в списке, а затем итеративно извлекать каждое действие для выполнения.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
public class Macro { private final List actions; public Macro() { actions = new ArrayList<>(); } public void record(Action action) { actions.add(action); } public void run() { actions.forEach(Action::perform); } } |
Заполняя макросы, мы можем добавить экземпляр каждой команды, которая была записана в объект макроса. Теперь простой запуск макроса вызовет каждую из команд по очереди. Это наш клиентский код.
1
2
3
4
5
|
Macro macro = new Macro(); macro.record( new OpenFile(fileReceiver)); macro.record( new WriteFile(fileReceiver)); macro.record( new CloseFile(fileReceiver)); macro.run(); |
Если бы вы были со мной до этого момента, вам было бы интересно, где лямбда-выражения вписываются во все это. На самом деле все наши командные классы, такие как OpenFile, WriteFile и CloseFile, на самом деле являются просто лямбда-выражениями, которые хотят вырваться из своих оболочек. Это просто поведение, которое передается как классы. Весь этот шаблон становится намного проще с лямбда-выражениями, потому что мы можем полностью покончить с этими классами. Давайте посмотрим, как класс Macro (клиент) может использовать лямбда-выражения вместо командных классов.
1
2
3
4
5
|
Macro macro = new Macro(); macro.record(() -> fileReceiver.openFile()); macro.record(() -> fileReceiver.writeFile()); macro.record(() -> fileReceiver.closeFile()); macro.run(); |
Это может быть улучшено, если принять во внимание тот факт, что каждое из этих лямбда-выражений выполняет один вызов метода. Таким образом, ссылки на методы могут быть использованы напрямую.
1
2
3
4
5
|
Macro macro = new Macro(); macro.record(fileReceiver::openFile); macro.record(fileReceiver::writeFile); macro.record(fileReceiver::closeFile); macro.run(); |
Шаблон команд легко расширяется, и в приемники можно добавлять новые методы действий для создания новых реализаций команд без изменения клиентского кода. Runnable интерфейс (java.lang.Runnable) в JDK — это популярный интерфейс, в котором используется шаблон Command. В этом блоге я попытался выразить шаблон команды в лямбда-выражении Java 8. Вы бы увидели, что с помощью лямбда-выражений требуется гораздо меньше шаблонов, что приводит к более чистому коду.
Этот пост был вдохновлен статьей « Использование шаблона команды с лямбда-выражениями » Ричарда Уорбертона.
Ссылка: | Java 8 лямбда-выражение для шаблонов проектирования — шаблон проектирования команд от нашего партнера по JCG Гурприта Сачдева в блоге gssachdeva . |