当前位置: 首页 > 知识库问答 >
问题:

项目Reactor:以相同的顺序与另一个发布者反应性地转换可变对象的状态

汤博
2023-03-14

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

到目前为止,我通常(ab)使用持平地图来获得预期的行为:

java prettyprint-override">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;
        }
    }
}

虽然这种< code > flat map -模式有效,但我觉得应该有一种更具反应性的解决方案。考虑到仅在< code>Mono中就有如此多的操作符,为了改变其状态而从一个对象“映射”到同一个对象,直觉上感觉是错误的。然而,“副作用”操作符(< code>doOn*)不允许在没有订阅的情况下轻易转换另一个发布者。

如果我的问题没有微不足道的解决方案,我非常愿意接受设计改进,因为代码的设计仍然是顺序的。

共有1个答案

咸亦
2023-03-14

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

这可能不是你想听到的答案,但是反应性的解决方案是完全抛弃可变性。在更复杂的例子中,在反应链中传递可变对象可能会导致意外的副作用,这可能会导致一些很难跟踪的错误。完全重构可变性要容易得多。

如果我的问题没有微不足道的解决方案,我非常愿意进行设计改进

我认为“最少变化”的方法是:

  • Make ExpensiveObject immutable. Remove the setter method, and provide another constructor that takes an explicit value for expensiveField.
  • Provide a "reactive" withExpensiveField() (or ofExpensiveField(), or something else entirely, take your pick!) method on ExpensiveObject that takes a Mono<Integer> for expensiveField and returns a Mono<ExpensiveObject>.
  • This then allows you to build your reactive chain with a single flatMap() call, and no mutable objects in sight:
    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);
            }
        }
    }
    

    您可能希望根据最终设计更改上述内容(例如方法在单个字段对象上没有多大意义),但这会跨越主要思想。

 类似资料:
  • 我有一张这样的地图<代码>地图 钥匙是数字1,2,3,4。。。学生对象是: 我想做的是把它转换成地图 我可以使用这些代码对地图进行分组,但summingDouble不适用于BigDecimal。此外,我无法将我的studentMap转换为StudentInfo地图:( 我的学生信息对象是:

  • 作为项目Reactor的用户,也想使用Spring集成,我想执行以下操作,这将以这样的方式工作: 一开始,我认为解决方案是执行以下错误代码: 当然,由于问题,它无法工作。我想知道如何一个接一个地执行操作(例如,在CockroachDB写入完成之前不要继续脉冲星写入,如果第一次操作失败,请停止这些消息的流)。 我正在考虑使用Spring集成事务支持,但我担心它在Retor中的使用。 我还看到有一种叫

  • 从静态初始值设定项初始化对象引用 将对它的引用存储到volatile字段或atomicreference 将对它的引用存储到正确构造的对象的最后一个字段 将对它的引用存储到由锁正确保护的字段中。 但是,我对第二个成语感到困惑。因为只能保证引用对另一个线程是可见的,但它没有它所引用的对象构造的同步。那么它如何保证可变对象是正确构造的,构造这个对象的线程是什么,被另一个线程打断了呢?

  • 问题内容: 我如何测试python中两个JSON对象是否相等,而忽略列表的顺序? 例如 … JSON文档a: JSON文档b: 并且应该比较相等,即使列表的顺序不同。 问题答案: 如果你想要两个具有相同元素但顺序不同的对象相等,那么显而易见的事情是比较它们的排序后的副本-例如,以JSON字符串和表示的字典: …但这是行不通的,因为在每种情况下,”errors”顶层dict的项都是具有相同元素的列表

  • 问题内容: 我如何测试python中两个JSON对象是否相等,而忽略列表的顺序? 例如 … JSON文档 a : JSON文档 b : 并且即使列表的顺序不同,也应该比较相等。 问题答案: 如果要使两个具有相同元素但顺序不同的对象相等,那么显而易见的事情是比较它们的排序后的副本-例如,以JSON字符串和表示的字典: …但这是行不通的,因为在每种情况下,顶层dict的项都是具有相同元素的列表,但是顺

  • 及其不安全的发布: 可以抛出AssertionError,我同意。作者写道,这是因为不安全的出版,但另一方面没有答案:什么才是正确的出版方式?它们表示了4个安全发布习惯用语,但我不明白,为什么它们会在上面的情况下起作用: 要安全地发布对象,必须同时使对对象的引用和对象的状态对其他线程可见。通过以下方法可以安全地发布构造正确的对象: null 这里是我的第一个问题,谢谢你的帮助!