Функциональный вариант получения значения из if-elseif-else в Java - PullRequest
0 голосов
/ 22 января 2019

Мне пришлось обработать некоторый JSON, который мог прийти в несколько разных форматах (и где мне нужно было только подмножество данных JSON), и я использовал JsonPointer (от Джексона), чтобы запросить JSON. Я написал нефункциональное решение проблемы, которое сработало для меня, но я хотел попробовать функциональный подход для целей обучения. В тестовой программе ниже вы можете увидеть мои два решения. Они оба работают, но функциональное решение стало довольно многословным, и я получил досадное предупреждение от Intellij относительно использования get () без проверки isPresent. Я хотел бы видеть предложения по улучшению функциональной реализации, и я счастлив видеть решения, использующие сторонние библиотеки. Я полагаю, что основная проблема здесь заключается в том, как моделировать if-else-if-else, где каждая ветвь должна возвращать какое-то значение функциональным способом.

@Test
public void testIt() {
    ObjectMapper om = new ObjectMapper();
    ImmutableList.of(
            "{ \"foo\": { \"key\": \"1\" } }",
            "{ \"bar\": { \"key\": \"1\" } }",
            "{ \"key\": \"1\" }")
            .forEach(str -> {
                try {
                    System.out.println("Non-functional: " + getNode(om.readTree(str)));
                    System.out.println("Functional: " + getNodeFunc(om.readTree(str)));
                } catch (Exception e) {
                    throw new RuntimeException("", e);
                }
            });
}

private JsonNode getNode(JsonNode parentNode) {
    JsonPointer jp1 = JsonPointer.compile("/foo");
    JsonPointer jp2 = JsonPointer.compile("/bar");
    if (!parentNode.at(jp1).isMissingNode()) {
        return parentNode.at(jp1);
    } else if (!parentNode.at(jp2).isMissingNode()) {
        return parentNode.at(jp2);
    }
    return parentNode;
}

private JsonNode getNodeFunc(JsonNode parentNode) {
    BiFunction<JsonNode, String, Optional<JsonNode>> findNode = (node, path) -> {
        JsonPointer jp = JsonPointer.compile(path);
        return node.at(jp).isMissingNode() ? Optional.empty() : Optional.of(node.at(jp));
    };

    return findNode.apply(parentNode, "/foo")
            .map(Optional::of)
            .orElseGet(() -> findNode.apply(parentNode, "/bar"))
            .map(Optional::of)
            .orElse(Optional.of(parentNode))
            .get(); // Intellij complains here: Optional.get() without isPresent check
}

Ответы [ 2 ]

0 голосов
/ 23 января 2019

Просто подумайте, функция getNode является идеальным функциональным кодом:

  • Выход зависит только от входных параметров и внутреннего алгоритма.
  • Он не имеетпобочные эффекты (он ничего не читает из внешнего мира и ничего не пишет во внешний мир)
  • Он всегда будет возвращать один и тот же вывод с одного и того же входа.
0 голосов
/ 22 января 2019

Я бы переписал его на

private JsonNode getNodeFunc2(JsonNode parentNode) {
    return Stream.of(JsonPointer.compile("/foo"), JsonPointer.compile("/bar"))
                 .filter(i -> !parentNode.at(i).isMissingNode())
                 .findFirst()
                 .map(parentNode::at)
                 .orElse(parentNode);
}

или

private JsonNode getNodeFunc3(JsonNode parentNode) {
    return Stream.of(JsonPointer.compile("/foo"), JsonPointer.compile("/bar"))
                 .map(parentNode::at)
                 .filter(Predicate.not(JsonNode::isMissingNode))
                 .findFirst()
                 .orElse(parentNode);
}

или

private JsonNode getNodeFunc4(JsonNode parentNode) {
    return Stream.of("/foo", "/bar")
                 .map(JsonPointer::compile)
                 .map(parentNode::at)
                 .filter(Predicate.not(JsonNode::isMissingNode))
                 .findFirst()
                 .orElse(parentNode);
}

, потому что кусок

if (!parentNode.at(jp1).isMissingNode()) {
    return parentNode.at(jp1);
} else if (!parentNode.at(jp2).isMissingNode()) {
    return parentNode.at(jp2);
}

дублирование кода и может быть аккуратно обработано циклом:

for (JsonPointer jsonPointer : jsonPointers) {
    JsonNode kid = parentNode.at(jsonPointer);
    if (!kid.isMissingNode()) {
         return kid;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...