Как реализовать этот вложенный поток с опциями?
У меня есть метод, который принимает 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 возможных выхода:
- optA пусто -> "ERR_1"
- optA не пусто && optB пусто -> "ERR_2"
- оба не пустые -> 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);