Je viens de commencer avec RxJava , l'implémentation Java de ReactiveX (également connu sous le nom de Rx et Reactive Extensions ). Quelque chose qui m'a vraiment frappé était la taille massive de de RxJava Flowable classe : il a 460 méthodes!
Être juste:
De nombreuses méthodes sont surchargées, ce qui augmente considérablement le nombre total de méthodes.
Peut-être que cette classe devrait être interrompue, mais ma connaissance et ma compréhension de RxJava sont très limitées. Les gens qui ont créé RxJava sont sûrement très intelligents, et ils peuvent sans doute proposer des arguments valables pour choisir de créer Flowable avec autant de méthodes.
D'autre part:
RxJava est l'implémentation Java des extensions réactives de Microsoft , et qui n'a même pas de classe Flowable , il ne s'agit donc pas de porter aveuglément une classe existante et de l'implémenter en Java.
[ Mise à jour: le point précédent en italique est incorrect dans les faits: la classe Observable de Microsoft , qui compte plus de 400 méthodes, a été utilisée comme base de la classe Observable de RxJava , et Flowable est similaire à Observable mais gère la contre-pression pour de gros volumes de données. Ainsi , l'équipe RxJava ont été portage d' une classe existante. Ce poste aurait dû être contester la conception originale de la Observable classe par Microsoft plutôt que de RxJava Flowable classe.]
RxJava n'a qu'un peu plus de 3 ans, donc ce n'est pas un exemple de code mal conçu en raison d'un manque de connaissances sur les bons principes de conception de classe ( SOLID ) (comme c'était le cas avec les premières versions de Java).
Pour une classe aussi grande que Flowable, sa conception semble intrinsèquement erronée, mais peut-être pas; une réponse à cette question SE Quelle est la limite du nombre de méthodes d'une classe? a suggéré que la réponse soit " Avoir autant de méthodes que nécessaire ".
Il est clair que certaines classes ont légitimement besoin d'un bon nombre de méthodes pour les prendre en charge indépendamment de la langue, car elles ne se décomposent pas facilement en quelque chose de plus petit et elles ont un bon nombre de caractéristiques et d'attributs. Par exemple: chaînes, couleurs, cellules de feuille de calcul, jeux de résultats de base de données et requêtes HTTP. Avoir peut-être quelques dizaines de méthodes pour que les classes représentent ces choses ne semble pas déraisonnable.
Mais Flowable a-t-il vraiment besoin de 460 méthodes, ou est-ce si énorme qu'il s'agit nécessairement d'un exemple de mauvaise conception de classe?
[Pour être clair: cette question concerne spécifiquement de RxJava Flowable classe plutôt que des objets Dieu en général.]
Réponses:
TL; DL
Le manque de fonctionnalités de langage de Java par rapport à C # ainsi que les considérations de découvrabilité nous ont fait placer les opérateurs source et intermédiaires dans de grandes classes.
Conception
Le Rx.NET d'origine a été développé en C # 3.0 qui a deux fonctionnalités cruciales: les méthodes d'extension et les classes partielles. Le premier vous permet de définir des méthodes d'instance sur d'autres types qui semblent alors faire partie de ce type cible tandis que les classes partielles vous permettent de diviser les grandes classes en plusieurs fichiers.
Aucune de ces fonctionnalités n'était ou n'est présente dans Java, par conséquent, nous avons dû trouver un moyen d'utiliser RxJava de manière suffisamment pratique.
Il existe deux types d'opérateurs dans RxJava: de type source représentés par des méthodes d'usine statiques et de type intermédiaire représentés par des méthodes d'instance. Les premiers pouvaient vivre dans n'importe quelle classe et donc ils auraient pu être répartis sur plusieurs classes d'utilité. Ce dernier nécessite de travailler sur une instance. En théorie, tout cela pourrait être exprimé par des méthodes statiques avec l'amont comme premier paramètre.
En pratique, cependant, le fait d'avoir plusieurs classes d'entrée rend la découverte de fonctionnalités par les nouveaux utilisateurs peu pratique (rappelez-vous, RxJava a dû apporter un nouveau concept et paradigme de programmation en Java) ainsi que l'utilisation de ces opérateurs intermédiaires un peu comme un cauchemar. Par conséquent, l'équipe d'origine a proposé la conception de l'API dite fluide: une classe qui contient toutes les méthodes statiques et d'instance et représente une source ou une étape de traitement à elle seule.
En raison de la nature de première classe des erreurs, de la prise en charge de la concurrence et de la nature fonctionnelle, on peut trouver toutes sortes de sources et de transformations concernant le flux réactif. Comme la bibliothèque (et le concept) ont évolué depuis l'époque de Rx.NET, de plus en plus d'opérateurs standard ont été ajoutés, ce qui, par nature, a augmenté le nombre de méthodes. Cela a conduit à deux plaintes habituelles:
Écrire des opérateurs réactifs est une tâche difficile que peu de gens maîtrisent au fil des ans; la plupart des utilisateurs de bibliothèques typiques ne peuvent pas créer d'opérateurs par eux-mêmes (et il n'est souvent pas vraiment nécessaire d'essayer). Cela signifie que de temps en temps, nous ajoutons d'autres opérateurs à l'ensemble standard. En revanche, nous avons rejeté beaucoup plus d'opérateurs en raison de leur caractère trop spécifique ou simplement d'une commodité qui ne peut pas tirer son propre poids.
Je dirais que la conception de RxJava a grandi de manière organique et pas vraiment selon certains principes de conception tels que SOLID. Il est principalement motivé par l'utilisation et la convivialité de son API fluide.
Autres relations avec Rx.NET
J'ai rejoint le développement RxJava fin 2013. Pour autant que je sache, les premières versions initiales de 0.x étaient en grande partie une réimplémentation de boîte noire où les noms et signatures des
Observable
opérateurs Rx.NET ainsi que quelques décisions architecturales ont été réutilisés. Cela impliquait environ 20% des opérateurs Rx.NET. La principale difficulté à l'époque était la résolution des différences de langage et de plate-forme entre C # et Java. Avec beaucoup d'efforts, nous avons réussi à implémenter de nombreux opérateurs sans regarder le code source de Rx.NET et nous avons porté les plus compliqués.En ce sens, jusqu'à RxJava 0.19, notre
Observable
était équivalent à Rx.NETIObservable
et à sesObservable
méthodes d'extension associées . Cependant, le soi-disant problème de contre-pression est apparu et RxJava 0.20 a commencé à diverger de Rx.NET au niveau du protocole et de l'architecture. Les opérateurs disponibles ont été étendus, beaucoup sont devenus sensibles à la contre-pression et nous avons introduit de nouveaux types:Single
etCompletable
à l'ère 1.x, qui n'ont pas d'homologues dans Rx.NET pour l'instant.La prise de conscience de la contre-pression complique considérablement les choses et le 1.x l'a
Observable
reçue après coup. Nous avons juré allégeance à la compatibilité binaire, donc changer le protocole et l'API n'était pas possible.Il y avait un autre problème avec l'architecture de Rx.NET: l'annulation synchrone n'est pas possible car pour cela, il faut que le
Disposable
soit retourné avant que l'opérateur ne commence à s'exécuter. Cependant, des sources comme celles quiRange
étaient impatientes ne reviennent pas avant d'avoir fini. Ce problème peut être résolu en injectant unDisposable
dans leObserver
au lieu d'en renvoyer unsubscribe()
.RxJava 2.x a été repensé et réimplémenté à partir de zéro dans ce sens. Nous avons un type distinct, sensible à la contre-pression,
Flowable
qui offre le même ensemble d'opérateurs queObservable
.Observable
ne prend pas en charge la contre-pression et est quelque peu équivalent à Rx.NETObservable
. En interne, tous les types réactifs injectent leur poignée d'annulation à leurs consommateurs, permettant à l'annulation synchrone de fonctionner efficacement.la source
Bien que j'admette que je ne connais pas la bibliothèque, j'ai jeté un coup d'œil à la classe Flowable en question et il semble qu'elle agisse comme une sorte de hub. En d'autres termes, il s'agit d'une classe destinée à valider l'entrée et à répartir les appels en conséquence dans le projet.
Cette classe ne serait donc pas vraiment considérée comme un objet divin car un objet divin est celui qui tente de tout faire. Cela fait très peu en termes de logique. En termes de responsabilité unique, le seul travail de la classe pourrait être de déléguer le travail dans toute la bibliothèque.
Donc, naturellement, une telle classe nécessiterait une méthode pour chaque tâche possible dont vous auriez besoin d'une
Flowable
classe dans ce contexte. Vous voyez le même type de modèle avec la bibliothèque jQuery en javascript, où la variable$
a toutes les fonctions et variables nécessaires pour effectuer des appels sur la bibliothèque, bien que dans le cas de jQuery, le code n'est pas simplement délégué mais aussi une bonne logique est exécuté à l'intérieur.Je pense que vous devriez faire attention à créer une classe comme celle-ci, mais elle a sa place tant que le développeur se souvient qu'il ne s'agit que d'un concentrateur et qu'il ne se convertit donc pas lentement en un objet divin.
la source
L'équivalent de RX de .NET
Flowable
est Observable . Il possède également toutes ces méthodes, mais elles sont statiques et utilisables comme méthodes d'extension . Le point principal de RX est que la composition est écrite en utilisant une interface fluide .Mais pour que Java ait une interface fluide, il a besoin que ces métodes soient des méthodes d'instance, car les méthodes statiques ne composeraient pas bien et il n'a pas de méthodes d'extension pour rendre les méthodes statiques composables. Donc, dans la pratique, toutes ces méthodes peuvent devenir des méthodes statiques, à condition que vous n'ayez pas à utiliser une syntaxe d'interface fluide.
la source