【问题标题】:Project Reactor: Reactively transform mutable object's state with another Publisher in same sequenceProject Reactor:以相同顺序与另一个发布者进行反应式转换不可变对象状态
【发布时间】:2019-10-29 11:19:23
【问题描述】:

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

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

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

【问题讨论】:

    标签: java reactive-programming project-reactor reactor


    【解决方案1】:

    虽然这种 flatMap 模式有效,但我觉得应该有一个更具反应性的解决方案。

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

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

    我将采取的“最少更改”方法是:

    • 使ExpensiveObject 不可变。移除 setter 方法,并提供另一个构造函数,该构造函数采用 expensiveField 的显式值。
    • ExpensiveObject 上提供一个“反应性”withExpensiveField()(或ofExpensiveField(),或其他完全不同的东西,任您选择!)方法,该方法采用Mono&lt;Integer&gt;expensiveField 并返回一个Mono&lt;ExpensiveObject&gt;
    • 然后,这允许您使用单个 flatMap() 调用构建您的反应链,并且看不到可变对象:
      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 方法对单个字段对象没有多大意义),但这会影响主要思想。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多