Паттерны появились, потому что многие разработчики искали пути повышения гибкости и степени повторного использования своих программ. Найденные решения воплощены в краткой и легко применимой на практике форме.
Особенностью данного издания является уникальный способ подачи материала, выделяющий серию «Head First» издательства O’Reilly в ряду множества скучных книг, посвященных программированию. Далле представлен отрывок «Упрощение кода RemoteControl с лямбда-выражениями»
Как вы уже видели, паттерн Команда достаточно прямолинеен. Тем не менее, языка Java предоставляет удобный инструмент, который позволяет еще сильнее упростить код — а именно лямбда-выражения. Лямбда-выражение представляет собой сокращенную форму записи метода (как последовательности вычислений) точно в том месте, где она используется. Вместо того, чтобы создавать отдельный класс с методом, создавать экземпляр этого класса, а затем вызывать метод, вы просто указываете: «Вот метод, который нужно вызвать» при помощи лямбда-выражения. В нашем случае вызываться должен метод execute().
Чтобы понять, как это делается, заменим объекты LightOnCommand и LightOffCommand
лямбда-выражениями. Выполните следующие действия, чтобы команды включения/выключения света реализовались лямбда-выражениями вместо объектов команд:
Шаг 1. Создание приемника
Шаг 2. Реализация команд пульта с использованием лямбда-выражений
Здесь происходит все самое интересное. Теперь вместо того, чтобы создавать объекты LightOnCommand и LightOffCommand для передачи remoteControl.setCommand(), мы просто передаем вместо каждого объекта лямбда-выражение с кодом из соответствующего метода execute():
Шаг 3. Активация кнопок
Этот шаг тоже не изменился — если не считать того, что при вызове метода onButtonWasPushed(0) команда в слоте 0 представлена объектом функции (созданным при помощи лямбда-выражения). Когда мы вызываем execute() для команды, этот метод сопоставляется с методом, определяемым лямбда-выражением, который затем выполняется.
Мы используем лямбда-выражения для замены объектов Command, а интерфейс Command содержит всего один метод: execute(). Лямбда-выражение, которое мы используем, должно иметь совместимую сигнатуру — так оно и есть: метод execute() не получает аргументов (как и наше лямбда-выражение) и не возвращает значения (как и наше лямбда-выражение). Компилятор всем доволен. Лямбда-выражение передается в параметре Command метода setCommand():
Запомните: если интерфейс параметра, в котором передается лямбда-выражение, содержит один (и только один!) метод, и сигнатура этого метода совместима с сигнатурой лямбда-выражения, все сработает как надо.
Ссылки на методы
Чтобы еще сильнее упростить код, можно воспользоваться ссылками на методы. Если передаваемое лямбда-выражение вызывает всего один метод, вместо лямбда-выражения можно
передать ссылку на метод. Это делается так:
Вместо того чтобы передавать лямбда-выражение, которое вызывает метод on() объекта
livingRoomLight, мы передаем ссылку на сам метод.
А что, если лямбда-выражение должно выполнять сразу несколько операций?
Иногда лямбда-выражения, используемые для замены объектов Command, не ограничиваются одной операцией. Давайте посмотрим, как заменить объекты stereoOnWithCDCommand и
stereoOffCommand лямбда-выражениями. Далее будет приведен полный код RemoteLoader, чтобы вы поняли, как все эти идеи сочетаются друг с другом.
Объект stereoOffCommand выполняет всего одну простую команду:
stereo.off();
Для этой команды вместо лямбда-выражения можно использовать ссылку на метод stereo::off.
Но stereoOnWithCDCommand выполняет не одну, а три операции:
stereo.on();
stereo.setCD();
stereo.setVolume(11);
В таком случае использовать ссылку на метод не удастся. Вместо этого придется либо записывать лямбда-выражение во встроенном виде, либо создать его отдельно, присвоить имя, а потом передать методу setCommand() объекта remoteControl по имени. Вариант с созданием
отдельного лямбда-выражения и присвоением ему имени выглядит так:
Command stereoOnWithCD = () -> {
stereo.on(); stereo.setCD(); stereo.setVolume(11);
};
remoteControl.setCommand(3, stereoOnWithCD, stereo::off);
Обратите внимание: Command используется как тип лямбда-выражения. Лямбда-выражение
сопоставляется с методом execute() интерфейса Command и с параметром Command, в кото-
ром оно передается методу setCommand().
Тест-драйв команд с использованием лямбда-выражений
Чтобы использовать лямбда-выражения для упрощения кода исходной реализации
RemoteControl (без отмены), мы изменим код RemoteLoader и заменим конкретные объекты
Command лямбда-выражениями. Также необходимо изменить конструктор RemoteControl
для использования лямбда-выражений вместо объекта NoCommand. После этого мож-
но удалить все конкретные классы Command (LightOnCommand, LightOffCommand,
HottubOnCommand, HottubOffCommand и т. д.). Вот и все! Все остальное остается преж-
ним. Не удалите случайно интерфейс Command; он необходим для сопоставления типа
объектов функций, создаваемых лямбда-выражениями, которые сохраняются в объекте
пульта и передаются различным методам.
Новый код класса RemoteLoader выглядит так:
public class RemoteLoader {
public static void main(String[] args) {
RemoteControl remoteControl = new RemoteControl();
Light livingRoomLight = new Light("Living Room");
Light kitchenLight = new Light("Kitchen");
CeilingFan ceilingFan = new CeilingFan("Living Room");
GarageDoor garageDoor = new GarageDoor("Main house");
Stereo stereo = new Stereo("Living Room");
Удаляем весь код создания конкретных объектов Command (вместе с самими классами). Код становится куда более компактным (а от 22 классов остается только 9).
remoteControl.setCommand(0, livingRoomLight::on, livingRoomLight::off);
remoteControl.setCommand(1, kitchenLight::on, kitchenLight::off);
remoteControl.setCommand(2, ceilingFan::high, ceilingFan::off);
Command stereoOnWithCD = () -> {
stereo.on(); stereo.setCD(); stereo.setVolume(11);
};
remoteControl.setCommand(3, stereoOnWithCD, stereo::off);
remoteControl.setCommand(4, garageDoor::up, garageDoor::down);
Мы используем ссылки на методы повсюду, где используются простые команды из одного метода, и полные лямбда-выражения там, где одного вызова метода недостаточно.(Ссылку на метод можно рассматривать как компактное лямбда-выражение. По сути это одно и то же; ссылка на метод — просто сокращенная запись для лямбда-выражения, которое вызывает всего один метод.)
System.out.println(remoteControl);
remoteControl.onButtonWasPushed(0);
remoteControl.offButtonWasPushed(0);
remoteControl.onButtonWasPushed(1);
remoteControl.offButtonWasPushed(1);
remoteControl.onButtonWasPushed(2);
remoteControl.offButtonWasPushed(2);
remoteControl.onButtonWasPushed(3);
remoteControl.offButtonWasPushed(3);
И не забудьте: мы должны изменить конструктор RemoteControl, удалить из него код конструирования объектов NoCommand и заменить его лямбда-выражениями.
public class RemoteControl {
Command[] onCommands;
Command[] offCommands;
public RemoteControl() {
onCommands = new Command[7];
offCommands = new Command[7];
for (int i = 0; i < 7; i++) {
onCommands[i] = () -> { };
offCommands[i] = () -> { };
}
}
// Остальной код
}
А теперь проверим результаты всех этих команд с лямбда-выражениями...
» Более подробно с книгой можно ознакомиться на сайте издательства
» Оглавление
» Отрывок
Для Хаброжителей скидка 20% по купону — Head First
Комментарии (6)
sergrt
29.03.2018 12:58ph_piter, будут ли «Чистая архитектура. Искусство разработки программного обеспечения» и «С++17 STL. Стандартная библиотека шаблонов» в электронном виде? Контакты на сайте молчат с ответами на такие вопросы.
И в качестве пожелания — доставка по Петербургу за 175 р. — это, конечно, здорово, если бы не срок в 2 недели. Опция «доставить хорошей курьерской компанией (1-2 рабочих дня, 250р)» была бы очень кстати.
ph_piter Автор
29.03.2018 13:15Чистая архитектура будет 9 апреля, С++17 — 17 или 24 апреля.
По Спб доходит за неделю, мы сейчас переезжаем на новый склад, срок доставки сократится на треть.
Matvey-Kuk
30.03.2018 13:02Меня искренне удивляет, что вы до сих пор используете такое оформление обложек. Мне оно кажется совершенно неуместным. Хочешь купить умную книгу, а на полке какой-то глянцевый журнал.
IvanVakhrushev
30.03.2018 13:45Лично я практически не могу читать книги серии Head First из-за их вырвиглазной верстки, крайне не комфортно.
vmm86
В чём отличия этого издания от предыдущего, и существенны ли они для покупки?
ph_piter Автор
Отличие в том, что полностью обновлены все примеры на Java.