System.reactive 反应式编程的底线在哪里

System.reactive 反应式编程的底线在哪里,system.reactive,rx-java,rx-android,System.reactive,Rx Java,Rx Android,我已经在我的项目中使用RxJava大约一年了。 随着时间的推移,我变得非常喜欢它-现在我想可能太多了 我现在写的大多数方法都有某种形式的Rx,这太棒了!(直到它不是)。 我现在注意到,有些方法需要大量的工作来组合不同的可观测生成方法。 我有一种感觉,虽然我理解了我现在写的东西,但下一个程序员将很难理解我的代码 在我讲到底线之前,让我直接从Kotlin中的代码中给出一个示例(不要深入探讨): private fun getCachedEntities( getManyFunc:()->可观察, g

我已经在我的项目中使用RxJava大约一年了。 随着时间的推移,我变得非常喜欢它-现在我想可能太多了

我现在写的大多数方法都有某种形式的Rx,这太棒了!(直到它不是)。 我现在注意到,有些方法需要大量的工作来组合不同的可观测生成方法。 我有一种感觉,虽然我理解了我现在写的东西,但下一个程序员将很难理解我的代码

在我讲到底线之前,让我直接从Kotlin中的代码中给出一个示例(不要深入探讨):

private fun getCachedEntities(
getManyFunc:()->可观察,
getFromNetwork:()->可观察,
getFunc:(字符串)->可观察,
insertFunc:(T)->单位,
updateFunc:(T)->单位,
deleteFunc:(字符串)->单位)
=concat(
getManyFunc().filter{isNew(it.timestampMillis)}
.map{ListResult(it.value,“”)},
getFromNetwork().doOnNext{
与存储同步(it.entities、getFunc、insertFunc、updateFunc、deleteFunc)
}).first()
.onErrorResumeNext{e->//如果发生网络错误,请返回缓存的数据和错误
concat(getManyFunc().map{ListResult(it.value,“”)},错误(e))
}
简单地说,它的作用是:

  • 从存储器中检索一些时间戳数据
    • 如果数据不是新的,则从网络获取数据
      • 再次将网络数据与存储器同步(以更新)
    • 如果发生网络错误,请再次检索旧数据和错误
我的实际问题来了: 反应式编程提供了一些非常强大的概念。但正如我们所知,权力越大,责任越大

我们的底线在哪里?用令人敬畏的反应式OneLiner填满我们的整个程序可以吗?还是我们应该把它保存起来,只用于真正平凡的操作

显然,这是非常主观的,但我希望有更多经验的人能够分享他的知识和陷阱。 让我说得更清楚些

我如何设计我的代码,使之既能反应又易于阅读


我发现在编写Rx(或任何稍微复杂的/新技术)时,有两件事我会牢记在心

  • 我能测试一下吗
  • 我能很容易地雇用一个能维护它的人吗。不是挣扎着去维护它,而是独自去维护它会好吗
  • 为此,我还发现,仅仅因为你可以,并不总是意味着你应该。作为指导,我尽量避免创建超过7行代码的查询。如果查询比这个大,我会尝试将其分解为我编写的子查询


    如果您提供的代码是代码库的核心,并且处于复杂性的极端末端,那么它可能是好的。然而,如果你发现你所有的Rx代码都那么复杂,你可能会创建一个很难使用的代码库。

    当你拿起Rx时,它会变得非常闪亮,一切看起来都像生锈的钉子,只等着你插进去

    就我个人而言,我认为最大的线索在于名称,反应式框架。给定一个需求,您需要考虑反应式解决方案是否真正有意义

    在任何Rx方案中,您都希望引入一个或多个事件流,并针对事件执行一些操作

    我认为有两个关键问题需要问:

    • 您是否控制事件流
    • 您必须以事件流的速率在多大程度上完成响应
    如果无法控制事件流,并且必须以事件流的速率响应,则Rx是一个很好的候选

    在任何其他情况下,这可能是一个糟糕的选择

    我见过很多例子,人们为了证明Rx的合理性而跳出圈套,制造出一种缺乏控制的错觉——这在我看来似乎很疯狂。为什么要放弃你的控制权

    一些例子:

  • 您必须从固定的文件列表中提取数据并将其存储在数据库中。您决定将每个文件名推送到一个主题中,并创建一个反应式管道,打开每个文件并投影数据,然后以某种方式处理数据,最后将其写入数据库

    这使控制测试和速率测试失败。迭代文件并将其拉入并尽可能快地处理它们会容易得多。“决定推动”这句话是这里的赠品

  • 您需要显示证券交易所的股票价格

    显然,这是Rx的一个好选择。如果你总的来说跟不上价格,你就完蛋了。您可能会将价格混为一谈(可能是每秒钟只提供一次更新),但这仍然符合保持价格不变的条件。你不能做的一件事就是要求证券交易所放慢速度

  • 这些(现实世界的)例子几乎落在光谱的两端,并没有太多的灰色区域。但是有很多灰色地带控制不清楚

    有时,在客户机/服务器系统中,您戴着客户机帽子,很容易落入牺牲控制的陷阱,或者将控制放在错误的位置,这可以通过正确的设计很容易解决。考虑这一点:

  • 客户端应用程序显示来自服务器的新闻更新

    • 新闻更新可随时提交到服务器,并以大容量创建
    • 应按客户端设置的间隔刷新客户端
    • 刷新间隔可以随时更改,用户始终可以请求立即刷新
    • 客户端仅显示由用户指定的带有特定关键字标记的更新
    • 新闻更新有时很长,客户端不应存储新闻更新的全部内容,而应显示
      private fun <T : Entity> getCachedEntities(
            getManyFunc: () -> Observable<Timestamped<List<T>>>,
            getFromNetwork: () -> Observable<ListResult<T>>,
            getFunc: (String) -> Observable<Timestamped<T>>,
            insertFunc: (T) -> Unit,
            updateFunc: (T) -> Unit,
            deleteFunc: (String) -> Unit)
            = concat(
            getManyFunc().filter { isNew(it.timestampMillis) }
                .map { ListResult(it.value, "") },
            getFromNetwork().doOnNext {
              syncWithStorage(it.entities, getFunc, insertFunc, updateFunc, deleteFunc)
            }).first()
            .onErrorResumeNext { e ->  // If a network error occurred, return the cached data and the error
              concat(getManyFunc().map { ListResult(it.value, "") }, error(e))
            }