如何使用 spring webflux 在功能反应式 java 中使用多个 if else 编写复杂代码

问题描述 投票:0回答:2

我是 RXjava 函数式编程的新手。我正在写一个具有多个条件的后端点:

  1. 当发布端点被产品击中时
  2. 如果数据库中不存在登录用户的购物车,则创建一个新购物车
  3. 如果购物车已经存在于 couch base 提取记录并检查产品 JSON 给定的 productid 是否已经存在于产品 JSON 中。
  4. 如果在产品 JSON 中不可用,则必须创建新产品
  5. 购物车应使用新创建的 productId 进行更新
  6. 如果它存在于产品 JSON 中,则获取产品记录并验证数量。
  7. 如果数量相同则什么都不做
  8. 否则,更新记录。

我面临着在函数式编程中编写这些 if else 条件的挑战。尝试使用

switchifEmpty
并且无法在其中编写代码。

这里是示例代码。

public Mono<Product> createProduct(final Tuple2<String, productdto> tuple2) {
    final Product productdto = tuple2.getT2();
    return Mono.just(tuple2.getT1())
        .map(cartRepository::findById)
        .defaultIfEmpty(cartRepository.save(
            cart.builder()
                .id(tuple2.getT1())
                .build()))
        .flatMap(cartres -> cartres)
        .flatMap(cartres -> {
             final Product product = Product.builder()
                 .id(1234)
                 .productId(productDTO.getProductId())
                 .productName(productDTO.getProductName())
                 .build();
             return productRepository.save(product)
                 .map(saveCart -> cart.builder()
                     .id(cartres.getId()).build())
                 .flatMap(cartRepository::save);
        });
    }).then(Mono.just(productDto));
}
java spring functional-programming spring-webflux reactive
2个回答
1
投票

我知道这不是处理复杂逻辑的最佳方式,但这是我在使用 Spring WebFlux 进行开发时基本上一直在做的方式。

if-else 逻辑的问题是你无法退出反应链。如果你在

map
调用中返回一些东西,它就会被扔到下一个映射器中。我看到解决这个问题的唯一解决方案是使用异常来逃避反应链。

这就是为什么我扩展了

Exception
类并创建了
CustomException

public class CustomException extends Exception {
    public CustomException(String s) {
        super(s);
    }
}

我们现在可以使用我们的

CustomException
通过抛出错误并在反应链的末端捕获它们来实现复杂的 if-else 逻辑。

对于这个例子,我将使用一个简单的登录端点来演示如何使用它。

让我们先创建一个

LoginForm
记录来接收请求体。

public record LoginForm(String username, String password) {}

现在让我们使用

AccountRepository
实现一个简单的登录端点,然后使用“SessionRepository”创建一个会话(这两个存储库都没有实现,只是想象它们存在)。

@PostMapping("/login")
public Mono<String> logIn(@RequestBody LoginForm loginForm) {

    // Get the account using the username
    return accountRepository.getAccountByUsername(loginForm.username()).flatMap(account -> {
        
        // Check password
        if(!account.getPassword().equals(loginForm.password()) {

            // Return "Login failed!" using the CustomException we created
            return Mono.error(new CustomException("Login failed!"));
        }
        
        // Create session
        return sessionRepository.save(new Session(account.getId(), randomToken());
    }).map(session -> "Login successful! " + session.getToken())
        .onErrorResume(CustomException.class, e -> Mono.just(e.getMessage()))
        .onErrorReturn("Server error!");
}

我希望看到这个答案的人能够理解我是如何在这个例子中实现一些 if-else 逻辑的。如果您有任何补充,请在下方评论。


1
投票

我知道有一个复杂的 if-else-逻辑,您必须使用 Spring WebFlux 调用

.map()
.flatMap()
defaultIfEmpty()
的反应链来实现。

这些反应性运算符不能总是替代复杂的 if-else-logic。

实施 if-else-decisions 必须在您的

.map()
.flatMap()
处理程序中完成。

在您的情况下,您可以为每个步骤 1..8

 实施一个 
java.util.Function<>

或一个简单的方法

第一个示例具有通过从存储库加载或创建新购物车来为您提供购物车的功能,如下所示。

此外,它还展示了如何使用

zipWith()
运算符向反应链添加更多数据。

在这里,检查产品在数据库中是否存在,并将结果作为布尔值传递给下游。

基于此布尔值,您可以使用 if-else-statement 返回现有产品或创建新产品。


    public Mono<Product> createProduct(final Tuple2<String, Product> tuple2) {
        final Product productDTO = tuple2.getT2();
        return Mono.just(tuple2.getT1())
                //   .map(cartRepository::findById)
                //           .defaultIfEmpty(cartRepository.save(
                //               cart.builder()
                //                   .id(tuple2.getT1())
                //                   .build()))
                .flatMap(provideCart(tuple2)) // see implemented method further below

                .zipWith(productRepository.existsById(productDTO.getId()))
                .flatMap(maybeCreateProduct(productDTO)); // see implemented method further below
    }

    private Function<String, Mono<Cart>> provideCart(final Tuple2<String, Product> tuple2) {
        return id -> cartRepository.findById(id)
                .switchIfEmpty(cartRepository.save(
                        Cart.builder()
                                .id(tuple2.getT1())
                                .build()));
    }

    private Function<Tuple2<Cart, Boolean>, Mono<Product>> maybeCreateProduct(Product productDTO) {
        return cartresAndProductExists -> {
            Cart cart = cartresAndProductExists.getT1();
            Boolean productExists = cartresAndProductExists.getT2();
            if (productExists) {
                return productRepository.findById(productDTO.getProductId())
            } else {
                return productRepository.save(Product.builder()
                        .id("1234")
                        .productId(productDTO.getProductId())
                        .productName(productDTO.getProductName())
                        .build());
            }
        };
    }

必须小心保持代码的可读性并避免无休止的反应步骤链。

将复杂的步骤提取到单独的方法中,甚至可能首先重新考虑流程以得出更简单和简化的解决方案

© www.soinside.com 2019 - 2024. All rights reserved.