Каждые две недели в Armakuni мы проводим один день в свободное от работы с клиентами время, чтобы разработать методы работы и то, как мы работаем. На этой неделе мы с Томом Орамом решили изучить способы использования Java Optional, чтобы извлечь из этого максимальную пользу. Мотивация для этого упражнения была основана на нашем наблюдении за тем, как JavaOptional часто не используется на том же уровне полезности по сравнению с тем, как он используется в других языках, и, конечно же, чтобы получить лучшее представление о TDD и парном программировании.

Что мы пытаемся решить

Мы недовольны тем, как option используется в экосистеме Java.

Время жизни значения anOptional в Java, по-видимому, обычно существует только в ограниченной области, прежде чем оно будет распаковано с помощью anorElse или аналогичного. Это отличается от того, как этот тип используется в других языках (в основном функциональных), где распаковка откладывается до самого последнего возможного момента.

Использование методов map и flatMap для последовательности дополнительных операций

Одна из приятных особенностей типа Optional заключается в том, что map и flatMap позволяют построить отказоустойчивый конвейер операций. Например:

public Optional<String> forUser(UUID userId) {
       return Optional.ofNullable(userId)
                      .flatMap(userRepository::fetchById)
                      .flatMap(User::getProfilePic)
                      .map(renderer::render);    
}

Покажите, как мы обычно программируем, и обучаем TDD и парному программированию.

XP, TDD и парное программирование — это то, как мы работаем изо дня в день и как мы учим наших клиентов решать проблемы, с которыми они сталкиваются. Поэтому в этом упражнении мы хотели строго использовать эти практики. Это также была отличная возможность пообщаться и немного пообщаться, пока погода в Лондоне была не такой хорошей, как обычно.

Упражнение

Использование нулей

Мы написали простое приложение, которое извлекает пользователя из воображаемого репозитория и отображает изображение профиля в виде тега HTML. Для просмотра исходного кода нажмите здесь. Есть ряд шагов, которые могут завершиться ошибкой, например user not foundили profile picture not exists. В таких случаях очевидным сценарием является возврат null.

public String forUser(UUID userId) {
        if (userId == null) {
            return null;
        }
        User user = userRepository.fetchById(userId);
        if (Objects.isNull(user)) {
            return null;
        }
        return user.getProfilePic();
}

Ввести опционально

Затем мы решили использовать Optional вместо null. Это может выглядеть так сейчас

public String forUser(UUID userId) {
        if (userId == null) {
            return null;
        }
        Optional<User> user = userRepository.fetchById(userId);
        return user.flatMap(User::getProfilePic).orElse(null);
}

Как видите, код не так хорош, как мог бы быть, несмотря на то, что мы ввели flatMap, в то же время нам удалось удалить некоторые условные операторы.

квартираКарта

flatMap — это метод Optional, который применяет метод к значению внутри структуры данных, которая возвращает другой экземпляр Optional. Это очень полезно для последовательности действий, когда каждое из них может дать сбой.

"Исходный код"

Сеть Опционы

Следующим шагом, который мы предприняли, было обернуть ввод как Optional и chain все шаги. На этом этапе нам удается удалить условную логику.

public String forUser(UUID userId) {
        return Optional.ofNullable(userId)
                .flatMap(userRepository::fetchById)
                .flatMap(User::getProfilePic)
                .orElse(null);
}

Это зависит от вашего стиля кодирования, вы можете сказать, что это красиво и читабельно, или наоборот.

"Исходный код"

Необязательный в качестве параметра метода

Далее мы решили сделать forUserreturn anOptional

Optional<String> forUser(UUID userId) {...}

и сделайте Rendereraccept его в качестве аргумента

String render(Optional<String> path) {...}

И угадайте, что здесь происходит?

IDE, которую мы использовали, дала нам предупреждение о том, что передавать необязательный параметр в качестве параметра не рекомендуется.

Необязательный был разработан, чтобы предоставить ограниченный механизм для возвращаемых типов библиотечных методов, где должен был быть четкий способ представить «нет результата». Использование поля с типом java.util.Optional также проблематично, если класс должен быть сериализуемым, а java.util.Optional — нет.

ИДЕЯ JetBrains

"Исходный код"

Удаление необязательного параметра

Мы проиграли битву с соглашением Java о передаче Optional в качестве параметра метода. Однако мы по-прежнему можем писать код без nullпроверок, используя Optional для объединения forUserи renderвызовов.

String imgTag = getProfilePicture.forUser(userId)
                .map(renderer::render)
                .orElse(defaultPictureRenderer.defaultImage());

"Исходный код"

Что мы узнали

  • Необязательный, не решающий проблему Null, переданный в метод
  • Необязательный делает явным, что ничего не может быть возвращено, но добавляет путаницы относительно того, может ли он также быть нулевым.
  • Использование необязательного параметра метода считается плохой практикой