Recién estoy comenzando con RxJava , la implementación de ReactiveX en Java (también conocida como Rx y Reactive Extensions ). Algo que realmente me llamó la atención fue el enorme tamaño de la RxJava Fluido clase : tiene 460 métodos!
Para ser justo:
Hay muchos 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 seguramente son muy inteligentes, y presumiblemente pueden ofrecer argumentos válidos para elegir crear Flowable con tantos métodos.
Por otra parte:
RxJava es la implementación de Java de las Extensiones reactivas de Microsoft , y eso ni siquiera tiene una clase Flowable , por lo que no se trata de portar a ciegas una clase existente e implementarla en Java.
[ Actualización: el punto anterior en cursiva es incorrecto: la clase Observable de Microsoft , que tiene más de 400 métodos, se utilizó como base para la clase Observable de RxJava , y Flowable es similar a Observable pero maneja la contrapresión para grandes volúmenes de datos. Entonces, el equipo de RxJava estaba portando una clase existente. Este post debería haber sido un reto el diseño original de la observable clase por Microsoft en lugar de RxJava de Fluido clase.]
RxJava tiene solo 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 principios de diseño de clase buenos ( SOLIDOS ) (como fue el caso con las primeras versiones de Java).
Para una clase tan grande como Flowable, su diseño parece intrínsecamente incorrecto, pero tal vez no; una respuesta a esta pregunta SE ¿ Cuál es el límite para el número de métodos de una clase? sugirió que la respuesta es " Tenga tantos métodos como necesite ".
Claramente, hay algunas clases que legítimamente necesitan una buena cantidad de métodos para apoyarlas, independientemente del idioma, porque no se dividen fácilmente en nada más pequeño y tienen una buena cantidad de características y atributos. Por ejemplo: cadenas, colores, celdas de hojas de cálculo, conjuntos de resultados de bases de datos y solicitudes HTTP. Tener unas pocas docenas de métodos para que las clases representen esas cosas no parece irrazonable.
¿Pero Flowable realmente necesita métodos 460, o es tan grande que es necesariamente un ejemplo de diseño de mala clase?
[Para que quede claro: esta pregunta se refiere específicamente a la RxJava Fluido clase en lugar de objetos de Dios en general.]
fuente
Respuestas:
TL; DL
La falta de características del lenguaje de Java en comparación con C #, así como las consideraciones de capacidad de descubrimiento, nos hicieron poner a los operadores de origen e intermedios en grandes clases.
Diseño
El Rx.NET original se desarrolló 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 múltiples archivos.
Ninguna de estas características estaban o están presentes en Java, por lo tanto, tuvimos que encontrar una manera de hacer que RxJava sea lo suficientemente conveniente.
Hay dos tipos de operadores en RxJava: tipo fuente representado por métodos de fábrica estáticos y tipo intermedio representado por métodos de instancia. Los primeros podrían vivir en cualquier clase y, por lo tanto, podrían haberse distribuido a lo largo de múltiples clases de utilidad. Este último requiere una instancia para trabajar. En concepto, todo esto podría expresarse a través de métodos estáticos con el flujo ascendente 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 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 ideó el denominado diseño API fluido: una clase que contiene todos los métodos estáticos y de instancia y representa una fuente o etapa de procesamiento por sí mismo.
Debido a la naturaleza de los errores de primera clase, 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 más y más operadores estándar, lo que por naturaleza aumentó el recuento de métodos. Esto lleva a dos quejas habituales:
Escribir operadores reactivos es una tarea difícil que no mucha gente domina a lo largo de los años; La mayoría de los usuarios típicos de la biblioteca 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. Por el contrario, hemos rechazado muchos más operadores debido a que son demasiado específicos o simplemente una conveniencia que no puede soportar su propio peso.
Diría que el diseño de RxJava creció orgánicamente y no siguiendo ciertos principios de diseño como SOLID. Se basa principalmente en el uso y la sensación de su fluida API.
Otras relaciones con Rx.NET
Me uní al desarrollo de RxJava a fines de 2013. Por lo que puedo decir, las primeras versiones iniciales de 0.x fueron en gran parte una reimplementación de recuadro negro donde
Observable
se reutilizaron los nombres y firmas de los operadores de Rx.NET , así como algunas decisiones arquitectónicas. Esto involucró aproximadamente el 20% de los operadores de Rx.NET. La principal dificultad en ese entonces era la resolución de las diferencias de lenguaje 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 Rx.NETIObservable
y susObservable
métodos de extensión complementarios . Sin embargo, apareció el llamado problema de contrapresión y RxJava 0.20 comenzó a divergir de Rx.NET a nivel de protocolo y arquitectura. Los operadores disponibles se ampliaron, muchos se volvieron conscientes de la contrapresión y presentamos nuevos tipos:Single
yCompletable
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 lo
Observable
recibió como una ocurrencia tardía. 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 síncrona no es posible porque para hacer eso, uno necesita
Disposable
que se devuelva antes de que el operador comience a ejecutar. Sin embargo, fuentes comoRange
estaban ansiosas y no regresan hasta que terminan. Este problema se puede resolver inyectando unDisposable
enObserver
lugar de devolver uno desubscribe()
.RxJava 2.x fue rediseñado y reimplementado desde cero a lo largo de estas líneas. Tenemos un tipo independiente de contrapresión
Flowable
que ofrece el mismo conjunto de operadores queObservable
.Observable
no admite contrapresión y es algo equivalente a Rx.NETObservable
. 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.fuente
Si bien admito que no estoy familiarizado con la biblioteca, eché un vistazo a la clase Flowable en cuestión y parece que está actuando como un tipo de centro. En otras palabras, es una clase destinada a validar entradas y distribuir llamadas en consecuencia en el proyecto.
Por lo tanto, esta clase no se consideraría realmente un objeto de Dios, ya que un objeto de Dios es aquel 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.
Entonces, naturalmente, una clase así requeriría un método para cada tarea posible que requeriría de una
Flowable
clase en este contexto. Verá el mismo tipo de patrón con la biblioteca jQuery 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 solo se delega sino que también tiene una buena lógica se ejecuta dentro.Creo que deberías tener cuidado de hacer una clase como esta, pero tiene su lugar siempre que el desarrollador recuerde que es solo un centro y, por lo tanto, no se convierte lentamente en un objeto divino.
fuente
El equivalente de .NET's RX
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 una 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 tiene métodos de extensión para que los métodos estáticos sean componibles. Por lo tanto, en la práctica, todos esos métodos podrían convertirse en métodos estáticos, siempre y cuando estuvieras bien sin usar una sintaxis de interfaz fluida.
fuente