java - react 堆项目 : Reactively transform mutable object's state with another Publisher in same sequence

标签 java reactive-programming project-reactor reactor

我正在尝试通过重构一些当前阻塞的代码来学习响应式(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/

相关文章:

spring-webflux - Spring webflux 错误处理程序 : How to get the reactor context of the request in the error handler?

java - 响应式(Reactive) spring-data-solr 存储库

java - Gradle库具有不同的版本

java - 在图中找到 'connected components'

java - 为什么使用克隆进行防御性复制会带来安全问题?

javascript - Rx - 根据内容拆分 Observable(分组依据直到更改)

java - PublishSubject - 有没有办法执行 onNext()?

javascript - RxJS zip 在 forkJoin 时不起作用

java - Android 中的 OkHttpClient 给出 405 请求方法不支持错误?

project-reactor - 是否可以并行启动 Mono 并汇总结果