在反应流中放置参数验证的位置?

时间:2018-03-13 13:21:07

标签: java rx-java rx-java2 project-reactor reactive-streams

今天在办公室,我和我的同事们正在讨论确保计算在反应流中以声明方式完成的重要性,而不是热切地,并且在方法调用时必须进行计算。

为了解释起见,让我给出一个非常幼稚,但说明性的例子。我们说以下是错误的:

Single<Integer> getStream(String s) {
   Integer n = Integer.valueOf(s);
   return Single.just(n);
}

显然,该方法的用户可以:

getStream("A").returnOnError(error -> 0)
              .subscribe(System.out::println, System.err::println);

但是这段代码将在getStream("A")中急切地失败,我的流错误处理程序永远不会到达。反应流方法的用户不期望围绕其调用执行try-catch,因为流提供了其他机制来处理错误,对吧?

所以,我们建议我们应该做以下任何一种:

Single.defer(() -> Single.just(Integer.valueOf(s));
Single.fromCallable(() -> Integer.valueOf(s))
Sigle.create(subscriber -> { subscriber.onNext(Integer.valueOf(s)); }

然而,在接下来的代码审查中,我发现了类似的内容:

Single<Foo> getReactiveFoo(Bar bar, Baz baz, int price) {
     return Single.fromCallable(() -> {
           Objects.requireNonNull(bar, "bar should not be null");
           Objects.requireNonNull(baz, "baz should not be null");
           Preconditions.checkArgument(price > 0, "price should be > 0");
           //...
     });
}

所以,现在我觉得这很有趣,因为我一直认为NullPointerExceptionIllegalArgumentExceptionIndexOutOfBoundException等的这种类型的验证有意捕获程序员错误,我通常喜欢这些失败尽可能接近错误发生的地方。当你抛出任何这些时,你肯定希望程序立即失败并发出一个需要处理的错误信号,我不会因为这个问题而急切地尽可能地接近失败。 bug被引入。

因此,即使我们最初的讨论,我也觉得这些程序员错误验证属于流之外,并且在急切的方法调用中,即

Single<Foo> getReactiveFoo(Bar bar, Baz baz, int price) {
     Objects.requireNonNull(bar, "bar should not be null");
     Objects.requireNonNull(baz, "baz should not be null");
     Preconditions.checkArgument(price > 0, "price should be > 0");
     return Single.fromCallable(() -> {
           //...
     });
}

我希望这不是一个固执己见的问题。我想了解反应流社区是否对我们应该如何处理这类异常以及它们是否属于声明性反应流或命令式方法调用有意见。

也许有关于这个主题的好例子或参考资料,你可以建议我应该阅读。

1 个答案:

答案 0 :(得分:1)

我认为将此类验证视为前提条件是明智的,因此将它们失败可视为编程错误。这些应该快速失败并且努力失败,所以在我看来,投掷的方法是可行的。这就是RxJava和Reactor所做的事情。