我正在尝试通过重构一些当前阻塞的代码来学习响应式(Reactive)编程。我多次遇到在 Mono 序列中设置某些可变数据对象的状态而不订阅它的问题。在旧代码中,对象的字段值是由某些阻塞服务计算的,我现在也在 Mono
中执行此操作。
到目前为止,我通常(ab)使用 flatMap
来获得预期的行为:
initExpensiveObject().flatMap(expObj -> initExpensiveField(expObj).map(expField -> {
expObj.setExpensiveField(expField);
return expObj;
})).subscribe(expObj -> System.out.println("expensiveField: " + expObj.getExpensiveField()));
import reactor.core.publisher.Mono;
public class Main {
/**
* Expensive, lazy object instantiation
*/
public static Mono<ExpensiveObject> initExpensiveObject() {
return Mono.fromCallable(ExpensiveObject::new);
}
/**
* Expensive, async mapping (i.e. database access, network request):
* ExpensiveObject -> int
*/
public static Mono<Integer> initExpensiveField(ExpensiveObject expObj) {
return Mono.just(1);
}
public static class ExpensiveObject {
private int expensiveField = -1;
public int getExpensiveField() {
return expensiveField;
}
public void setExpensiveField(int expensiveField) {
this.expensiveField = expensiveField;
}
}
}
虽然这个 flatMap
模式有效,但我觉得应该有一个更具 react 性的解决方案。考虑到Mono
中有如此多的运算符单独而言,从一个对象“映射”到同一对象以改变其状态的直觉感觉是错误的。然而,“副作用”运算符 (doOn*
) 不允许在不订阅另一个发布者的情况下轻松转换它。
如果我的问题没有简单的解决方案,我非常愿意接受设计改进,因为代码的设计仍然是顺序。
最佳答案
While this flatMap-pattern works, I feel like there should be a more reactive solution.
这可能不是您想要听到的答案,但 react 性解决方案是完全放弃可变性。在更复杂的示例中,在 react 链中传递可变对象可能会导致无意的副作用,这可能会导致一些相当难以追踪的错误。完全重构可变性要容易得多。
I'm very much open to design improvements if there is no trivial solution to my problem
我会采取的“最少改变”方法是:
- 制造
ExpensiveObject
不可变的。删除 setter 方法,并提供另一个采用expensiveField
显式值的构造函数。 . - 提供“响应式(Reactive)”
withExpensiveField()
(或ofExpensiveField()
,或其他完全不同的东西,请选择!)ExpensiveObject
上的方法这需要Mono<Integer>
对于expensiveField
并返回Mono<ExpensiveObject>
. - 这样您就可以使用单个
flatMap()
构建 react 链调用,并且看不到可变对象:initExpensiveObject() .flatMap(expObj -> expObj.withExpensiveField(initExpensiveField(expObj))) .subscribe(expObj -> System.out.println("expensiveField: " + expObj.getExpensiveField()));
以上代码经过修改:
public class Main {
/**
* Expensive, lazy object instantiation
*/
public static Mono<ExpensiveObject> initExpensiveObject() {
return Mono.fromCallable(ExpensiveObject::new);
}
/**
* Expensive, async mapping (i.e. database access, network request):
* ExpensiveObject -> int
*/
public static Mono<Integer> initExpensiveField(ExpensiveObject expObj) {
return Mono.just(1);
}
public static final class ExpensiveObject {
private final int expensiveField;
public ExpensiveObject() {
expensiveField = -1;
}
private ExpensiveObject(int expensiveField) {
this.expensiveField = expensiveField;
}
public int getExpensiveField() {
return expensiveField;
}
public Mono<ExpensiveObject> withExpensiveField(Mono<Integer> expensiveField) {
return expensiveField.map(ExpensiveObject::new);
}
}
}
您可能希望根据最终设计更改上述内容(例如 with
方法对单个字段对象没有多大意义),但这概括了主要思想。
关于java - react 堆项目 : Reactively transform mutable object's state with another Publisher in same sequence,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58606433/