Книга «Head First. Паттерны проектирования. Обновленное юбилейное издание»

    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
    Поделиться публикацией
    Комментарии 8
    • +1
      В чём отличия этого издания от предыдущего, и существенны ли они для покупки?
      • 0
        Отличие в том, что полностью обновлены все примеры на Java.
      • 0
        ph_piter, будут ли «Чистая архитектура. Искусство разработки программного обеспечения» и «С++17 STL. Стандартная библиотека шаблонов» в электронном виде? Контакты на сайте молчат с ответами на такие вопросы.
        И в качестве пожелания — доставка по Петербургу за 175 р. — это, конечно, здорово, если бы не срок в 2 недели. Опция «доставить хорошей курьерской компанией (1-2 рабочих дня, 250р)» была бы очень кстати.
        • 0
          Чистая архитектура будет 9 апреля, С++17 — 17 или 24 апреля.
          По Спб доходит за неделю, мы сейчас переезжаем на новый склад, срок доставки сократится на треть.
          • +1
            Меня искренне удивляет, что вы до сих пор используете такое оформление обложек. Мне оно кажется совершенно неуместным. Хочешь купить умную книгу, а на полке какой-то глянцевый журнал.
            • +1
              Matvey-Kuk, такие обложки — фишка серии Head-First, а не нравится она обычно конченым занудам.
              • 0
                Если вы решили, что обложка мне не нравится, то это совсем не так. Прекрасная обложка! Именно она еще в студенческие годы отговорила меня и моих друзей покупать «питерские» книги с плохой бумагой, странным переводом и нелепой версткой. Еще, если я не ошибаюсь, книги были весьма большими и их нельзя было положить в сумку для нетбука. Особо аналогов не было, пришлось заказывать O'Reilly и выучить технический английский. Разве это не прекрасно?
            • +1
              Лично я практически не могу читать книги серии Head First из-за их вырвиглазной верстки, крайне не комфортно.

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое