RxJava类Flowable是否可以合法地使用460个方法?


14

我刚刚开始使用RxJava,它是Java对ReactiveX的实现 (也称为RxReactive Extensions)。一些真正让我吃惊的庞大规模RxJava的可流动:它具有460点的方法!

公平起见:

  • 有很多方法被重载,这大大增加了方法的总数。

  • 也许应该拆分此类,但是我对RxJava的知识和理解非常有限。谁创造RxJava的人肯定是非常聪明的,他们可以大概是选择创建提供有效的参数可流动有这么多的方法。

另一方面:

  • RxJava是Microsoft的Reactive Extensions的Java实现,甚至没有Flowable类,因此,这不是盲目移植现有类并用Java实现的情况。

  • [ 更新:斜体前一点是事实不正确:微软的可观察类,其中有超过400的方法,被用来作为RxJava的基础上可观察类,并且可流动相似,可观察到,但手柄背压的大量数据。因此,RxJava团队正在移植现有的类。这篇文章应该是挑战的原设计可观察到由微软而不是RxJava的类悬浮剂分类]

  • RxJava已有3年多的历史了,因此这并不是由于缺乏对良好(SOLID)类设计原理的了解而导致代码被错误设计的示例(与Java的早期版本一样)。

对于像Flowable这样大的类,其设计本质上是错误的,但也许不是。这个SE问题的一个答案一个类方法的数量限制是多少?建议答案是“ 拥有所需的多种方法 ”。

显然,有些类无论其语言如何,都合法地需要大量方法来支持它们,因为它们不会轻易分解为更小的东西,并且它们具有大量的特征和属性。例如:字符串,颜色,电子表格单元格,数据库结果集和HTTP请求。对于类来说,也许有几十种方法来表示这些东西似乎并不合理。

但是Flowable是否真的需要460种方法,还是它是如此之大以至于它必然是不良类设计的一个例子?

[需要明确的是:这个问题,特别是涉及RxJava的可流动类,而不是一般的神的对象。]



1
@gnat当然可以,但是不是重复的。这个问题是通用的,我的问题特别是涉及RxJava的可流动类。
Skomisa

@skomisa然后修复标题以适合您的问题。
欣快的

@Euphoric点。
Skomisa

1
这个问题很有趣并且很有根据。但是,我建议稍微改写一下,以便采用不太主观的风格(可能是由于最初的震惊;
Christophe

Answers:


14

TL; DL

与C#相比,Java语言特性的缺乏以及可发现性的考虑使我们将源和中间运算符划分为大型类。

设计

原始的Rx.NET是用C#3.0开发的,它具有两个关键功能:扩展方法和部分类。前者使您可以在其他类型上定义实例方法,这些实例方法随后似乎是该目标类型的一部分,而部分类允许您将大型类拆分为多个文件。

Java中既没有也不存在这些功能,因此,我们必须找到一种使RxJava足够方便地可用的方法。

RxJava中有两种运算符:静态工厂方法表示的源类运算符和实例方法表示的中间类运算符。前者可以存在于任何类中,因此它们可以分布在多个实用程序类中。后者需要处理一个实例。从概念上讲,所有这些都可以通过静态方法表示,其中上游作为第一个参数。

但是,实际上,具有多个条目类使新用户发现功能不方便(请记住,RxJava必须将新概念和编程范例引入Java),以及使用这些中间运算符有些噩梦。因此,最初的团队提出了所谓的fluent API设计:一个类包含所有静态方法和实例方法,并独自表示源或处理阶段。

由于错误的一流性质,并发支持和功能性质,人们可以提出与反应流有关的各种来源和转换。自Rx.NET时代以来,随着库(和概念)的发展,添加了越来越多的标准运算符,这自然增加了方法的数量。这导致了两个常见的抱怨:

  • 为什么有这么多方法?
  • 为什么没有方法X可以解决我非常特殊的问题?

编写响应式运算符是一项艰巨的任务,多年来没有多少人掌握过。大多数典型的库用户无法自己创建运算符(并且通常没有必要尝试)。这意味着我们会不时将更多运算符添加到标准集中。相比之下,由于过于具体或仅仅是无法发挥自身作用的便利,我们拒绝了更多运营商。

我想说RxJava的设计是有机发展的,并不是真正按照某些设计原则(例如SOLID)发展的。它主要是由其流畅的API的使用和感觉驱动的。

与Rx.NET的其他关系

我于2013年末加入RxJava开发。据我所知,最初的0.x早期版本很大程度上是黑盒实现,其中Rx.NET Observable运算符的名称和签名以及一些体系结构决策被重用。这涉及大约20%的Rx.NET运营商。当时的主要困难是解决C#和Java之间的语言和平台差异。通过努力,我们实现了许多运算符,而无需查看Rx.NET的源代码,并且移植了更复杂的运算符。

从这个意义上讲,直到RxJava 0.19为止,我们Observable都等效于Rx.NET IObservable及其配套的Observable扩展方法。但是,出现了所谓的背压问题,RxJava 0.20开始在协议和体系结构级别上与Rx.NET脱节。可用的经营者exteneded,许多成为背压感知和我们推出的新类型:SingleCompletable在1.x的时代,这在Rx.NET没有同行截至目前。

背压感知使事情变得相当复杂,而1.x Observable则将其作为事后考虑。我们发誓效忠于二进制兼容性,因此几乎不可能更改协议和API。

Rx.NET的体系结构还有另一个问题:同步取消是不可能的,因为这样做,需要Disposable在操作员开始执行之前返回。但是,诸如此类的消息源Range非常渴望,直到它们完成后才返回。可以通过将a Disposable注入Observer而不是从中返回a来解决此问题subscribe()

RxJava 2.x已按照这些原则从头开始进行了重新设计和重新实现。我们有一个单独的,可感知背压的类型Flowable,它提供与相同的运算符集ObservableObservable不支持背压,并且在某种程度上等同于Rx.NET的Observable。在内部,所有无功类型都将其取消句柄注入其使用者,从而使同步取消有效地工作。


10

虽然我承认我对库不熟悉,但是我看了一下有问题的Flowable类,它看起来像是各种枢纽。换句话说,它是一个类,用于验证输入并相应地在项目中分配调用。

因此,这一类并不是真正的上帝对象,因为上帝对象是试图做所有事情的对象。就逻辑而言,这做得很少。就单一职责而言,全班唯一的工作可以说是委派整个图书馆的工作。

因此,Flowable在这种情况下,此类很自然会为您需要的每个可能的任务提供一个方法。您会在javascript中看到与jQuery库相同的模式类型,其中的变量$具有执行对该库的调用所必需的所有功能和变量,尽管对于jQuery而言,代码不仅被简单委派,而且还具有很多逻辑在其中运行。

我认为您应该小心地制作这样的类,但是只要开发人员记住它只是一个集线器,并且因此不会慢慢转换为上帝对象,它就可以摆放它。


2
道歉,以消除对您的答案的接受,这既有益又有启发性,但是akarnokd随后的回应是来自RxJava团队的某人!
Skomisa

@skomisa如果这是我自己的问题,我别无所求了!没有冒犯!:)
尼尔

7

.NET的RX的相当于Flowable可观察到。它还具有所有这些方法,但它们是静态的,可用作扩展方法。RX的要点是使用流畅的界面编写构图。

但是要使Java具有流畅的接口,它需要将这些方法用作实例方法,因为静态方法无法很好地组合,并且它没有扩展方法可以使静态方法可组合。因此,实际上,只要您不使用流利的接口语法,所有这些方法都可以成为静态方法。


3
恕我直言,流畅的界面概念是针对语言限制的解决方法。实际上,您是在Java之上使用类来构建语言。如果您想一想即使是一种简单的编程语言中有多少个功能,并且不算所有重载的变体,那么很容易就能看出如何使用这么多方法。Java 8的功能特性可以解决导致这种设计的许多问题,而诸如Kotlin之类的现代语言正朝着能够获得相同好处的方向发展,而无需方法链。
JimmyJames

由于您的帖子,我进行了更深入的挖掘,似乎.NET的RX的Observable≈RxJava的Observable≈,所以我的问题的前提是不正确的。我已经相应地更新了OP。同样,RxJava的Flowable≈(可观察的+一些额外的用于并行化和处理反压的方法)。
Skomisa

@skomisa Java的Observable也有数百种方法。因此,您可以将两者进行比较。但是主要区别在于.NET的Observable是静态的,而所有方法都是静态的。而Java不是。那是巨大的差异。但实际上,它们的行为相同。
欣快的
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.