VAVR compose Try and List

Я пытаюсь понять, какие идиоматические способы использования VAVR's Try. Рассматриваемый мной вариант использования состоит из следующих шагов:
- получить список обуви (вызов может вызвать проверенное исключение);
- очистить каждую обувь (вызов может вызвать проверенное исключение); - восстановить каждую обувь (вызов может вызвать проверенное исключение)
- вернуть список очищенной / восстановленной обуви

Вот мой пример кода игрушки, в котором метод processRequest покупает n пар обуви, чистит и восстанавливает их; выводит ошибки, если они есть:

// definitions
ShoeStore java.util.List<Shoe> buy(int numberOfPairs) throws OutOfStockException;
ShoeCleaningService Shoe clean(Shoe dirtyShoe) throws OutOfShoePolishException;
ShoeRestoreService Shoe restore(Shoe oldShoe) throws OutOfSparePartsException;

class EnterpriseShoeService {
    // constructor
    ...

    public List<Shoe> processRequest(int numberOfPairs) {
        Try<List<Shoe>> shoes = Try.of(() -> shoeStore.buy(numberOfPairs));
        Try<List<Try<Shoe>>> cleanedAndRestoredShoes = shoes.map(xs -> xs.stream().map(shoe ->
                Try.success(shoe)
                        .andThenTry(shoeCleaningService::clean)
                        .andThenTry(shoeRestoreService::restore))
                .collect(Collectors.toList()));

        List<Shoe> result = cleanedAndRestoredShoes
                .getOrElseGet(err -> {
                    System.out.println(err.getMessage());
                    return Collections.emptyList();
                })
                .stream()
                .map(shoeTry -> shoeTry.onFailure(err -> System.out.println(err.getMessage())))
                .filter(Try::isSuccess)
                .map(Try::get)
                .collect(Collectors.toList());

        return result;

    }
}

У меня вопрос: как можно упростить эту логику? есть ли какие-либо вызовы методов, которые можно было бы исключить? можно ли улучшить читаемость?


person dgt    schedule 09.05.2019    source источник
comment
Мне нравится тот факт, что вы представляете игрушечный пример, чтобы выделить проблему, с которой сталкиваетесь, но все еще сложно реконструировать из кода то, что вы пытаетесь достичь. В дополнение к размещению кода, который у вас получился, вы можете добавить список требований. Может быть, в некоторых пунктах указано, что вы хотели бы видеть, если есть ошибка при доставке обуви, что, если есть ошибка при чистке / восстановлении обуви, и как бы вы хотели объединить список получившейся обуви, когда она была ошибка в некоторых ботинках, но не во всех (короткое замыкание?).   -  person Nándor Előd Fekete    schedule 10.05.2019
comment
Я считаю, что вам следует сосредоточиться в первую очередь на интерфейсе, который вы хотели бы иметь для этого решения. Что делать, если обуви не хватает? Это все или ничего? Вы назвали свой вопрос функциональным программированием - в связи с этим вы должны хотя бы разделить свою функцию на несколько хорошо названных функций.   -  person Damian0o    schedule 10.05.2019
comment
Например, ›Тип возврата не имеет никакого смысла, поскольку вы фильтруете только успешный запрос, поэтому Список‹ Попробуйте ‹Shoe› ›будет содержать только Список‹ Успех ‹Shoe› ›. Мы можем удалить эту оболочку Try из типа returing. Тип имеет значение в FP - ваш пример полон ошибок компиляции.   -  person Damian0o    schedule 10.05.2019
comment
У меня уже есть несколько функций: купить, почистить, восстановить. Теперь я хотел бы составить их в удобочитаемой форме.   -  person dgt    schedule 10.05.2019
comment
Что-то похожее на: buy (). FlatMap (shoes - ›shoes.stream) .map (shoe -› clean (shoe)). Map (shoe- ›restore (shoe)). Collect (list). Моя мотивация - написать удобочитаемое решение. В Java лямбда-выражения и проверенные исключения неудобно составлять, поэтому я смотрю на Option (al), Try, Either.   -  person dgt    schedule 10.05.2019
comment
Много чего происходит. У вас есть очень сложный метод, который делает все. Вавр используется только в рамках этого метода. Может для начала попробуй вернуть попробуй от купить чистые методы восстановления. Это позволит вам что-нибудь почистить.   -  person Damian0o    schedule 11.05.2019


Ответы (1)


Я не знаю, все ли работает так, как ожидалось, поскольку никаких требований не упоминалось, но это должно дать вам представление о силе декомпозиции.

import io.vavr.collection.List;
import io.vavr.control.Try;


public class TryListComposition {

   ShoeStore store;

   ShoeCleaningService cleaningService;

   ShoeRestoreService restoreService;

   public java.util.List<Shoe> processRequest(int numberOfPairs) {
    return processShoesRequest(numberOfPairs).getOrElse(List.empty()).toJavaList();
   }

   public Try<List<Shoe>> processShoesRequest(int numberOfPairs) {
      return this.buy(numberOfPairs)
            .map(shoes -> shoes
                    .map(this::cleanAndRestore)
                    .flatMap(x -> x)
            );
   }

   public Try<Shoe> cleanAndRestore(Shoe shoe) {
      return clean(shoe).flatMap(this::restore);
   }


   Try<List<Shoe>> buy(int numberOfPairs) {
      return Try.of(() -> 
        List.ofAll(store.buy(numberOfPairs).stream());
   }

   Try<Shoe> clean(Shoe dirtyShoe) {
      return Try.of(() -> cleaningService.clean(dirtyShoe));
   }

   Try<Shoe> restore(Shoe oldShoe) {
      return Try.of(() -> restoreService.restore(oldShoe));
   }

}

class Shoe {

}

interface ShoeStore {
   java.util.List<Shoe> buy(int numberOfPairs) throws 
   OutOfStockException;
}

interface ShoeCleaningService {
   Shoe clean(Shoe dirtyShoe) throws OutOfShoePolishException;
}

interface ShoeRestoreService {
   Shoe restore(Shoe oldShoe) throws OutOfSparePartsException;
}

class OutOfStockException extends Exception {

}

class OutOfShoePolishException extends Exception {

}

class OutOfSparePartsException extends Exception {

}
person Damian0o    schedule 11.05.2019