¿Puede la clase RxJava Flowable legítimamente tener 460 métodos?

14

Acabo de comenzar con RxJava , la implementación de Java de ReactiveX (también conocido como Rx y Reactive Extensions ). Algo que realmente me impresionó fue el tamaño masivo de clase Flowable de RxJava : tiene 460 metodos!

Para ser justos:

  • Hay una gran cantidad de métodos que están sobrecargados, lo que aumenta significativamente el número total de métodos.

  • Quizás esta clase debería dividirse, pero mi conocimiento y comprensión de RxJava es muy limitado. Las personas que crearon RxJava son seguramente muy inteligentes, y presumiblemente pueden ofrecer argumentos válidos para elegir crear Flowable con tantos métodos.

Por otro lado:

  • RxJava es la implementación de Java de Reactive de Microsoft Extensiones , y eso ni siquiera tiene una clase Flowable , por lo que no es un caso de portar ciegamente una clase existente e implementarla en Java.

  • [ Actualización: El punto anterior en cursiva es objetivamente incorrecto: la clase Observable de Microsoft, que tiene más de 400 métodos, se usó como base para La clase strong> Observable y Flowable es similar a Observable pero maneja la contrapresión para grandes volúmenes de datos. Así que el equipo de RxJava estaba portando una clase existente. Esta publicación debería haber desafiado el diseño original de la clase Observable de Microsoft en lugar de la clase Flowable de RxJava.]

  • RxJava tiene solo un poco más de 3 años, por lo que este no es un ejemplo de código mal diseñado debido a la falta de conocimiento sobre el bien ( SOLID ) principios de diseño de clase (como fue el caso con las primeras versiones de Java).

Para una clase tan grande como Flowable , su diseño parece inherentemente incorrecto, pero tal vez no; una respuesta a esta pregunta de SE ¿Qué es? ¿El límite al número de métodos de una clase? sugirió que la respuesta es " Tenga tantos métodos como necesite ".

Claramente, hay algunas clases que necesitan legítimamente un buen número de métodos para respaldarlas independientemente del idioma, porque no se dividen fácilmente en nada más pequeño y tienen un buen número de características y atributos. Por ejemplo: cadenas, colores, celdas de hoja de cálculo, conjuntos de resultados de base de datos y solicitudes HTTP. Tener tal vez unas pocas docenas de métodos para que las clases representen esas cosas no parece irrazonable.

¿Pero Flowable realmente necesita 460 métodos, o es tan grande que es necesariamente un ejemplo de diseño de clase deficiente?

[Para ser claro: esta pregunta se relaciona específicamente con la clase Flowable de RxJava en lugar de con los objetos de Dios en general.]

    
pregunta skomisa 25.01.2018 - 08:53

3 respuestas

14

TL; DL

La falta de características de lenguaje de Java en comparación con C #, así como las consideraciones de capacidad de detección, nos han hecho poner operadores de origen e intermedios en grandes clases.

Diseño

El Rx.NET original fue desarrollado en C # 3.0 que tiene dos características cruciales: métodos de extensión y clases parciales. El primero le permite definir métodos de instancia en otros tipos que luego parecen ser parte de ese tipo de destino, mientras que las clases parciales le permiten dividir clases grandes en varios archivos.

Ninguna de estas funciones estaba o está presente en Java, por lo tanto, tuvimos que encontrar una manera de hacer que RxJava sea lo suficientemente conveniente.

En RxJava hay dos tipos de operadores: de tipo fuente representados por métodos de fábrica estáticos y de tipo intermedio representados por métodos de instancia. Los primeros podrían vivir en cualquier clase y, por lo tanto, podrían haber sido distribuidos a lo largo de múltiples clases de utilidad. Este último requiere que se trabaje una instancia. En concepto, todo esto podría expresarse a través de métodos estáticos con el upstream como primer parámetro.

Sin embargo, en la práctica, tener múltiples clases de entrada hace que el descubrimiento de características por parte de los nuevos usuarios sea un inconveniente (recuerde, RxJava tuvo que traer un nuevo concepto y paradigma de programación a Java), así como el uso de esos operadores intermedios como una pesadilla. Por lo tanto, el equipo original creó el llamado diseño API fluido: una clase que contiene todos los métodos estáticos y de instancia y representa una etapa de procesamiento o fuente por sí misma.

Debido a la naturaleza de primera clase de los errores, el soporte de concurrencia y la naturaleza funcional, se pueden encontrar todo tipo de fuentes y transformaciones con respecto al flujo reactivo. A medida que la biblioteca (y el concepto) evolucionaron desde los días de Rx.NET, se agregaron cada vez más operadores estándar, lo que por naturaleza aumentó el conteo de métodos. Esto dio lugar a dos quejas habituales:

  • ¿Por qué hay tantos métodos?
  • ¿Por qué no hay un método X que resuelva mi problema muy particular?

Escribir operadores reactivos es una tarea difícil, no muchas personas dominan a lo largo de los años; la mayoría de los usuarios de bibliotecas típicos no pueden crear operadores por sí mismos (y, a menudo, no es realmente necesario que lo intenten). Esto significa que, de vez en cuando, agregamos más operadores al conjunto estándar. En contraste, hemos rechazado muchos más operadores debido a que somos demasiado específicos o simplemente una conveniencia que no puede hacer valer su propio peso.

Yo diría que el diseño de RxJava creció orgánicamente y no a lo largo de ciertos principios de diseño como SOLID. Se basa principalmente en el uso y la sensación de su API fluida.

Otras relaciones con Rx.NET

Me uní al desarrollo de RxJava a finales de 2013. Por lo que puedo decir, las primeras versiones iniciales de 0.x fueron en gran medida una reimplementación de caja negra donde los nombres y firmas de los operadores Rx.NET Observable , así como un Se reutilizaron pocas decisiones arquitectónicas. Esto implicó alrededor del 20% de los operadores Rx.NET. La principal dificultad en ese entonces era la resolución de las diferencias de idioma y plataforma entre C # y Java. Con gran esfuerzo, logramos implementar muchos operadores sin mirar el código fuente de Rx.NET y portamos los más complicados.

En este sentido, hasta RxJava 0.19, nuestro Observable era equivalente a IObservable de Rx.NET y sus métodos complementarios de extensión Observable . Sin embargo, apareció el llamado problema de la contrapresión y RxJava 0.20 comenzó a separarse de Rx.NET a nivel de protocolo y arquitectura. Los operadores disponibles se ampliaron, muchos tomaron conciencia de la contrapresión e introdujimos nuevos tipos: Single y Completable en la era 1.x, que no tienen contrapartes en Rx.NET a partir de ahora.

La conciencia de contrapresión complica las cosas considerablemente y el 1.x Observable lo recibió como una idea de último momento. Juramos lealtad a la compatibilidad binaria, por lo que no fue posible cambiar el protocolo y la API.

Hubo otro problema con la arquitectura de Rx.NET: la cancelación sincrónica no es posible porque para hacer eso, se necesita que se devuelva el Disposable antes de que el operador comience a ejecutar. Sin embargo, fuentes como Range estaban ansiosas y no regresan hasta que terminan. Este problema se puede resolver inyectando un Disposable en el Observer en lugar de devolver uno de subscribe() .

RxJava 2.x fue rediseñado y reimplementado desde cero en este sentido. Tenemos un tipo separado, que tiene en cuenta la contrapresión, Flowable , que ofrece el mismo conjunto de operadores que Observable . Observable no admite la contrapresión y es algo equivalente a Observable de Rx.NET. Internamente, todos los tipos reactivos inyectan su identificador de cancelación a sus consumidores, permitiendo que la cancelación síncrona funcione de manera eficiente.

    
respondido por el akarnokd 30.01.2018 - 11:06
10

Aunque admitiré que no estoy familiarizado con la biblioteca, eché un vistazo a la Clase fluida en cuestión y parece que está actuando como un centro de todo tipo. En otras palabras, es una clase destinada a validar la entrada y distribuir llamadas en consecuencia en el proyecto.

Por lo tanto, esta clase no sería realmente considerada como un objeto de Dios, ya que un objeto de Dios es uno que intenta hacer todo. Esto hace muy poco en términos de lógica. En términos de responsabilidad única, se podría decir que el único trabajo de la clase es delegar el trabajo en toda la biblioteca.

Así que, naturalmente, tal clase requeriría un método para cada tarea posible que requerirías de una clase Flowable en este contexto. Verá el mismo tipo de patrón con jQuery library en javascript, donde la variable $ tiene todas las funciones y variables necesarias para realizar llamadas en la biblioteca, aunque en el caso de jQuery, el código no es simplemente delegado sino que también es bueno. un poco de lógica se ejecuta dentro.

Creo que debes tener cuidado de hacer una clase como esta, pero tiene su lugar siempre y cuando el desarrollador recuerde que es solo un centro y, por lo tanto, no se convierta lentamente en un objeto divino.

    
respondido por el Neil 25.01.2018 - 09:06
7

El equivalente en RX de .NET a Flowable es Observable . También tiene todos esos métodos, pero son estáticos y se pueden usar como métodos de extensión . El punto principal de RX es que la composición se escribe usando interfaz fluida .

Pero para que Java tenga una interfaz fluida, necesita que esos métodos sean métodos de instancia, ya que los métodos estáticos no se compondrían bien y no tienen métodos de extensión para hacer que los métodos estáticos sean compostables. Entonces, en la práctica, todos esos métodos se pueden convertir en métodos estáticos, siempre y cuando esté bien sin utilizar una sintaxis de interfaz fluida.

    
respondido por el Euphoric 25.01.2018 - 09:04

Lea otras preguntas en las etiquetas