我是 RXjava 函数式编程的新手。我正在写一个具有多个条件的后端点:
我面临着在函数式编程中编写这些 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));
}
我知道这不是处理复杂逻辑的最佳方式,但这是我在使用 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 逻辑的。如果您有任何补充,请在下方评论。
我知道有一个复杂的 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());
}
};
}
必须小心保持代码的可读性并避免无休止的反应步骤链。
将复杂的步骤提取到单独的方法中,甚至可能首先重新考虑流程以得出更简单和简化的解决方案