Как реализовать этот вложенный поток с опциями?

У меня есть метод, который принимает String в качестве входных данных, а также должен возвращать String,

Следующее ASCII искусство представляет логический поток:

Option<A> optA = finder.findA(input);

          optA
           /\
isEmpty() /  \ isDefined()  
         /    \
 "ERR_1"       Option<B> optB = finder.findB(optA.get().bid);
                      / \
           isEmpty() /   \ isDefined()
                    /     \
                "ERR_2"    opt2.get().id

В основном для данного input я ищу A объект, который возвращается, завернутый в Option, Тогда это A присутствует я ищу B - завернутый в Option тоже, иначе вернусь ERR_1, Тогда если B присутствует вернуть это id, иначе вернуть ERR_2,

Мне интересно, как это может быть реализовано с использованием опций (или, может быть, сопоставление с образцом?) Красивым и кратким способом (без какой-либо ifology) - возможно, в одну строку.

Может ли кто-нибудь предложить что-нибудь?

Исходный код для тестирования можно найти здесь.

3 ответа

Решение

Похоже, у вас есть 3 возможных выхода:

  1. optA пусто -> "ERR_1"
  2. optA не пусто && optB пусто -> "ERR_2"
  3. оба не пустые -> optB.get().bid

Вы можете добиться этого, сделав это с Javaslang:

 optA
   .map(a -> finder.findB(a.bid)
      .map(b -> b.bid)
      .getOrElse("ERR_2"))
   .getOrElse("ERR_1");

Если optA пусто, мы будем прыгать прямо к orElse("ERR_1")

Если optA не пусто, мы используем значение, хранящееся внутри для получения значения b.bid или же "ERR_2" в случае optB пустота.

Кроме того, в чистой Java 8 это будет выглядеть так:

optA
  .map(a -> finder.findB(a.bid)
    .map(b -> b.bid)
    .orElse("ERR_2"))
  .orElse("ERR_1");

Так как вы используете javaslang, Try кажется лучшим выбором, потому что он распространяет ошибку через цепочку, в то время как Option распространяет только свою "пустоту".

Если вы можете изменить findA а также findB возвращать Try ты получаешь:

Try<B> b = finder.findA(input)
    .flatMap(a -> finder.findB(a.bid))

Если вы не можете, то:

Try<B> b = finder.findA(input).toTry(() -> new Exception("ERR_1"))
    .flatMap(a -> findB(a.bId).toTry(() -> new Exception("ERR_2")))

Это становится предварительным BЯ не уверен, если вы хотите свернуть действительное значение и ошибку в одно и то же значение, если это так, то:

String value = b.getOrElseGet(Throwable::getMessage)

Если у вас есть проблема с созданием бессмысленных исключений, вы можете использовать Either в каждой из операций поиска, где левым значением является тип вашей ошибки. Кажется, это лучше моделирует проблему, но может иметь обратную сторону длинных сигнатур, в зависимости от того, как вы разбили выражения.

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

Во-первых, выделите ошибки, которые вы хотите инкапсулировать для каждой точки отказа.

Supplier<? extends RuntimeException> missingAException = IllegalStateException::new;
Supplier<? extends RuntimeException? missingBException = IllegalStateException::new;

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

Теперь мы пишем опционные.

Optional<A> optA = finder.find(input);
Optional<B> optB = finder.findB(optA.orElseThrow(missingAException));

Извлекать optBиспользуйте тот же шаблон, что и для optA,

B value = optB.orElseThrow(missingBException);
Другие вопросы по тегам