image В мире постоянно кто-то сталкивается с такими же проблемами программирования, которые возникают и у вас. Многие разработчики решают совершенно идентичные задачи и находят похожие решения. Если вы не хотите изобретать велосипед, используйте готовые шаблоны (паттерны) проектирования, работе с которыми посвящена эта книга.

Паттерны появились, потому что многие разработчики искали пути повышения гибкости и степени повторного использования своих программ. Найденные решения воплощены в краткой и легко применимой на практике форме.

Особенностью данного издания является уникальный способ подачи материала, выделяющий серию «Head First» издательства O’Reilly в ряду множества скучных книг, посвященных программированию. Далле представлен отрывок «Упрощение кода RemoteControl с лямбда-выражениями»

Как вы уже видели, паттерн Команда достаточно прямолинеен. Тем не менее, языка Java предоставляет удобный инструмент, который позволяет еще сильнее упростить код — а именно лямбда-выражения. Лямбда-выражение представляет собой сокращенную форму записи метода (как последовательности вычислений) точно в том месте, где она используется. Вместо того, чтобы создавать отдельный класс с методом, создавать экземпляр этого класса, а затем вызывать метод, вы просто указываете: «Вот метод, который нужно вызвать» при помощи лямбда-выражения. В нашем случае вызываться должен метод execute().

Чтобы понять, как это делается, заменим объекты LightOnCommand и LightOffCommand
лямбда-выражениями. Выполните следующие действия, чтобы команды включения/выключения света реализовались лямбда-выражениями вместо объектов команд:

Шаг 1. Создание приемника
image

Шаг 2. Реализация команд пульта с использованием лямбда-выражений

Здесь происходит все самое интересное. Теперь вместо того, чтобы создавать объекты LightOnCommand и LightOffCommand для передачи remoteControl.setCommand(), мы просто передаем вместо каждого объекта лямбда-выражение с кодом из соответствующего метода execute():

image

Шаг 3. Активация кнопок

Этот шаг тоже не изменился — если не считать того, что при вызове метода onButtonWasPushed(0) команда в слоте 0 представлена объектом функции (созданным при помощи лямбда-выражения). Когда мы вызываем execute() для команды, этот метод сопоставляется с методом, определяемым лямбда-выражением, который затем выполняется.

image

Мы используем лямбда-выражения для замены объектов Command, а интерфейс Command содержит всего один метод: execute(). Лямбда-выражение, которое мы используем, должно иметь совместимую сигнатуру — так оно и есть: метод execute() не получает аргументов (как и наше лямбда-выражение) и не возвращает значения (как и наше лямбда-выражение). Компилятор всем доволен. Лямбда-выражение передается в параметре Command метода setCommand():

image

Запомните: если интерфейс параметра, в котором передается лямбда-выражение, содержит один (и только один!) метод, и сигнатура этого метода совместима с сигнатурой лямбда-выражения, все сработает как надо.

Ссылки на методы


Чтобы еще сильнее упростить код, можно воспользоваться ссылками на методы. Если передаваемое лямбда-выражение вызывает всего один метод, вместо лямбда-выражения можно
передать ссылку на метод. Это делается так:

image

Вместо того чтобы передавать лямбда-выражение, которое вызывает метод 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] = () -> { };
            }
      }
      // Остальной код
}

А теперь проверим результаты всех этих команд с лямбда-выражениями...

image

» Более подробно с книгой можно ознакомиться на сайте издательства
» Оглавление
» Отрывок

Для Хаброжителей скидка 20% по купону — Head First

Комментарии (6)


  1. vmm86
    29.03.2018 12:21
    +1

    В чём отличия этого издания от предыдущего, и существенны ли они для покупки?


    1. ph_piter Автор
      29.03.2018 12:35

      Отличие в том, что полностью обновлены все примеры на Java.


  1. sergrt
    29.03.2018 12:58

    ph_piter, будут ли «Чистая архитектура. Искусство разработки программного обеспечения» и «С++17 STL. Стандартная библиотека шаблонов» в электронном виде? Контакты на сайте молчат с ответами на такие вопросы.
    И в качестве пожелания — доставка по Петербургу за 175 р. — это, конечно, здорово, если бы не срок в 2 недели. Опция «доставить хорошей курьерской компанией (1-2 рабочих дня, 250р)» была бы очень кстати.


  1. ph_piter Автор
    29.03.2018 13:15

    Чистая архитектура будет 9 апреля, С++17 — 17 или 24 апреля.
    По Спб доходит за неделю, мы сейчас переезжаем на новый склад, срок доставки сократится на треть.


  1. Matvey-Kuk
    30.03.2018 13:02

    Меня искренне удивляет, что вы до сих пор используете такое оформление обложек. Мне оно кажется совершенно неуместным. Хочешь купить умную книгу, а на полке какой-то глянцевый журнал.


  1. IvanVakhrushev
    30.03.2018 13:45

    Лично я практически не могу читать книги серии Head First из-за их вырвиглазной верстки, крайне не комфортно.