Project Reactor:与另一个发布者以相同的顺序对可变对象的状态进行响应式转换

时间:2019-10-29 11:19:23

标签: java reactive-programming project-reactor reactor

我正在尝试通过重构一些当前阻塞的代码来学习反应式编程。我多次遇到过这样的问题,即从Mono序列中设置某些可变数据对象的状态而不订阅它。在旧代码中,对象的字段值是由某些阻塞服务计算的,我现在也在Mono s内部进行了

到目前为止,我通常一直在(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模式有效,但我认为应该有一个更具响应性的解决方案。考虑到Mono中仅存在如此多的运算符,从直观上感觉将一个对象“映射”到同一对象以改变其状态是错误的。但是,“副作用”运算符(doOn*)不允许在不订阅的情况下轻松转换其他发布者。

如果对我的问题没有简单的解决方案,我非常愿意进行设计改进,因为代码的设计仍然是 sequential

1 个答案:

答案 0 :(得分:1)

  

虽然这个flatMap模式有效,但我认为应该有一个更具响应性的解决方案。

可能不是您想听到的答案,但被动的解决方案是完全放弃变异性。在更复杂的示例中,将可变对象传递到反应链中会导致意外的副作用,这可能会导致某些相当难以跟踪的错误。完全重构可变性要容易得多。

  

如果没有解决问题的简单方法,我非常愿意进行设计改进

我将采用的“最小变化”方法是:

  • 使ExpensiveObject不可变。删除setter方法,并提供另一个为expensiveField带有显式值的构造函数。
  • withExpensiveField()上提供“反应式” ofExpensiveField()(或ExpensiveObject,或完全由您选择!)的方法,该方法需要{{1 }},并返回Mono<Integer>
  • 然后,您可以通过一次expensiveField调用来构建反应链,并且看不到任何可变对象:
    Mono<ExpensiveObject>

上面有修改的代码:

flatMap()

您可能要根据最终设计来更改上述内容(例如,initExpensiveObject() .flatMap(expObj -> expObj.withExpensiveField(initExpensiveField(expObj))) .subscribe(expObj -> System.out.println("expensiveField: " + expObj.getExpensiveField())); 方法对单个字段对象没有多大意义),但这涉及了主要思想。