Ich fange gerade mit RxJava an , Javas Implementierung von ReactiveX (auch bekannt als Rx und Reactive Extensions ). Was mich wirklich beeindruckt hat, war die enorme Größe der Flowable- Klasse von RxJava : Sie verfügt über 460 Methoden!
Um fair zu sein:
Es gibt viele Methoden, die überladen sind, was die Gesamtzahl der Methoden erheblich beeinträchtigt.
Vielleicht sollte diese Klasse aufgelöst werden, aber meine Kenntnisse und mein Verständnis von RxJava sind sehr begrenzt. Die Leute, die RxJava erstellt haben, sind sicherlich sehr schlau und sie können vermutlich gültige Argumente für die Entscheidung liefern , Flowable mit so vielen Methoden zu erstellen .
Auf der anderen Seite:
RxJava ist die Java-Implementierung von Microsofts Reactive Extensions , für die es nicht einmal eine Flowable- Klasse gibt. Es handelt sich also nicht darum , eine vorhandene Klasse blind zu portieren und in Java zu implementieren.
[ Update: Der vorherige kursive Punkt ist faktisch falsch: Die Observable- Klasse von Microsoft , die über 400 Methoden verfügt, wurde als Basis für die Observable- Klasse von RxJava verwendet , und Flowable ähnelt Observable , verarbeitet jedoch den Gegendruck für große Datenmengen. So das RxJava Team wurden eine vorhandene Klasse zu portieren. Dieser Beitrag hätte eher das ursprüngliche Design der Observable- Klasse von Microsoft als die Flowable-Klasse von RxJava in Frage stellen sollen .]
RxJava ist erst etwas älter als drei Jahre. Dies ist also kein Beispiel dafür, dass Code aufgrund mangelnder Kenntnisse über gute ( SOLID- ) Klassenentwurfsprinzipien falsch entworfen wurde (wie dies bei früheren Versionen von Java der Fall war).
Für eine so große Klasse wie Flowable scheint ihr Design von Natur aus falsch zu sein, aber vielleicht auch nicht. eine Antwort auf diese SE-Frage Was ist die Grenze für die Anzahl der Methoden einer Klasse? schlug vor, dass die Antwort " Haben Sie so viele Methoden, wie Sie brauchen ".
Es ist klar, dass es einige Klassen gibt, die zu Recht eine angemessene Anzahl von Methoden benötigen, um sie unabhängig von der Sprache zu unterstützen, da sie nicht leicht in kleinere Klassen zerfallen und eine angemessene Anzahl von Merkmalen und Attributen aufweisen. Zum Beispiel: Zeichenfolgen, Farben, Tabellenzellen, Datenbankergebnismengen und HTTP-Anforderungen. Vielleicht ein paar Dutzend Methoden für Klassen zu haben, um diese Dinge darzustellen, erscheint nicht unvernünftig.
Aber braucht Flowable wirklich 460 Methoden oder ist es so groß, dass es notwendigerweise ein Beispiel für schlechtes Klassendesign ist?
[Um klar zu sein: Diese Frage bezieht sich speziell auf die Flowable- Klasse von RxJava und nicht auf Objekte von Gott im Allgemeinen.]
quelle
Antworten:
TL; DL
Das Fehlen von Java-Sprachfunktionen im Vergleich zu C # sowie Überlegungen zur Erkennbarkeit haben uns veranlasst, Quell- und Zwischenoperatoren in große Klassen einzuteilen.
Design
Das ursprüngliche Rx.NET wurde in C # 3.0 entwickelt, das zwei entscheidende Merkmale aufweist: Erweiterungsmethoden und Teilklassen. Mit ersteren können Sie Instanzmethoden für andere Typen definieren, die dann Teil dieses Zieltyps zu sein scheinen, während Sie mit Teilklassen große Klassen in mehrere Dateien aufteilen können.
Keine dieser Funktionen war oder ist in Java vorhanden, daher mussten wir einen Weg finden, um RxJava so komfortabel wie möglich zu nutzen.
In RxJava gibt es zwei Arten von Operatoren: quellenähnlich, dargestellt durch statische Factory-Methoden, und zwischenähnlich, dargestellt durch Instanzmethoden. Ersteres könnte in jeder Klasse leben und somit über mehrere Versorgungsklassen verteilt sein. Letzteres setzt voraus, dass an einer Instanz gearbeitet wird. Konzeptionell könnten diese alle über statische Methoden mit dem Upstream als erstem Parameter ausgedrückt werden.
In der Praxis erschwert das Vorhandensein mehrerer Einstiegsklassen das Erkennen von Funktionen durch neue Benutzer (denken Sie daran, RxJava musste ein neues Konzept und Programmierparadigma in Java einführen) und die Verwendung dieser Zwischenoperatoren, was ein Albtraum ist. Aus diesem Grund hat das ursprüngliche Team das sogenannte flüssige API-Design entwickelt: Eine Klasse, die alle statischen Methoden und Instanzmethoden enthält und eine Quell- oder Verarbeitungsstufe für sich darstellt.
Aufgrund der erstklassigen Art von Fehlern, der Unterstützung von Parallelität und der Funktionsweise können alle Arten von Quellen und Transformationen in Bezug auf den reaktiven Fluss gefunden werden. Als sich die Bibliothek (und das Konzept) seit den Tagen von Rx.NET weiterentwickelten, kamen immer mehr Standardoperatoren hinzu, was die Anzahl der Methoden von Natur aus erhöhte. Dies führte zu zwei üblichen Beschwerden:
Reaktive Operatoren zu schreiben ist eine schwierige Aufgabe, die nicht viele Menschen im Laufe der Jahre gemeistert haben. Die meisten typischen Bibliotheksbenutzer können keine Operatoren selbst erstellen (und oft ist es nicht wirklich notwendig, dass sie es versuchen). Dies bedeutet, dass wir von Zeit zu Zeit weitere Operatoren zum Standardsatz hinzufügen. Im Gegensatz dazu haben wir viel mehr Bediener abgelehnt, weil sie zu spezifisch sind oder einfach nur eine Annehmlichkeit, die ihr eigenes Gewicht nicht tragen kann.
Ich würde sagen, das Design von RxJava ist organisch gewachsen und entspricht nicht wirklich bestimmten Designprinzipien wie SOLID. Es wird hauptsächlich von der Verwendung und dem Gefühl der fließenden API bestimmt.
Andere Beziehungen zu Rx.NET
Ich bin Ende 2013 der RxJava-Entwicklung beigetreten. Soweit ich weiß, waren die ersten frühen 0.x-Versionen größtenteils eine Black-Box-Neuimplementierung, bei der die Namen und Signaturen von Rx.NET-
Observable
Betreibern sowie einige Architekturentscheidungen wiederverwendet wurden. Dies betraf etwa 20% der Rx.NET-Betreiber. Die Hauptschwierigkeit war damals die Auflösung von Sprach- und Plattformunterschieden zwischen C # und Java. Mit viel Aufwand ist es uns gelungen, viele Operatoren zu implementieren, ohne den Quellcode von Rx.NET zu betrachten, und die komplizierteren zu portieren.In diesem Sinne entsprach our
Observable
bis zu RxJava 0.19 den Methoden von Rx.NETIObservable
und seinen Companion-Observable
Erweiterungen. Das sogenannte Gegendruckproblem trat jedoch auf und RxJava 0.20 begann sich von Rx.NET auf Protokoll- und Architekturebene zu unterscheiden. Die verfügbaren Operatoren wurden erweitert, viele wurden auf den Gegendruck aufmerksam und wir führten neue Typen ein:Single
undCompletable
in der 1.x-Ära, die ab sofort keine Gegenstücke in Rx.NET haben.Das Bewusstsein für den Gegendruck erschwert die Dinge erheblich und der 1.x
Observable
hat es nachträglich erhalten. Wir haben der Binärkompatibilität die Treue geschworen, sodass das Ändern des Protokolls und der API meistens nicht möglich war.Es gab ein weiteres Problem mit der Architektur von Rx.NET: Eine synchrone Löschung ist nicht möglich, da dazu die
Disposable
Rückgabe erforderlich ist , bevor der Bediener mit der Ausführung beginnt. Quellen wieRange
waren jedoch eifrig und kehren nicht zurück, bis sie fertig sind. Dieses Problem kann gelöst werden, indem einDisposable
in das injiziert wird,Observer
anstatt eines von zurückzugebensubscribe()
.RxJava 2.x wurde überarbeitet und von Grund auf neu implementiert . Wir haben einen separaten Typ,
Flowable
der den Gegendruck berücksichtigt und die gleichen Operatoren bietet wieObservable
.Observable
unterstützt keinen Gegendruck und ist etwas äquivalent zu Rx.NETObservable
. Intern geben alle reaktiven Typen ihren Widerrufscode an ihre Verbraucher weiter, sodass der synchrone Widerruf effizient funktioniert.quelle
Ich gebe zwar zu, dass ich mit der Bibliothek nicht vertraut bin, habe mir aber die fragliche Flowable-Klasse angesehen und es scheint, als würde sie sich wie eine Art Drehscheibe verhalten . Mit anderen Worten, es ist eine Klasse, die Eingaben validieren und Aufrufe im Projekt entsprechend verteilen soll.
Diese Klasse würde daher nicht wirklich als ein Gottobjekt betrachtet, da ein Gottobjekt eines ist, das versucht, alles zu tun. Das hat wenig mit Logik zu tun. In Bezug auf die Einzelverantwortung kann gesagt werden, dass die einzige Aufgabe der Klasse darin besteht, die Arbeit in der gesamten Bibliothek zu delegieren.
Daher würde eine solche Klasse natürlich eine Methode für jede mögliche Aufgabe erfordern, die Sie
Flowable
in diesem Kontext von einer Klasse benötigen würden . Sie sehen den gleichen Mustertyp bei der jQuery-Bibliothek in Javascript, wo die Variable$
alle Funktionen und Variablen enthält, die zum Ausführen von Aufrufen auf die Bibliothek erforderlich sind. Bei jQuery ist der Code jedoch nicht einfach delegiert, sondern auch ein gutes Stück Logik wird innerhalb von ausgeführt.Ich denke, Sie sollten darauf achten, eine Klasse wie diese zu erstellen, aber sie hat ihren Platz, solange sich der Entwickler daran erinnert, dass sie nur ein Hub ist und sich daher nicht langsam in ein Gott-Objekt verwandelt.
quelle
Das RX-Äquivalent von .NET
Flowable
ist Observable . Es hat auch alle diese Methoden, aber sie sind statisch und können als Erweiterungsmethoden verwendet werden . Der Hauptpunkt von RX ist, dass die Komposition über eine flüssige Oberfläche geschrieben wird .Damit Java jedoch über eine flüssige Schnittstelle verfügt, müssen diese Metoden Instanzmethoden sein, da statische Methoden nicht gut zusammengesetzt sind und es keine Erweiterungsmethoden gibt, mit denen statische Methoden zusammengesetzt werden können. In der Praxis könnten all diese Methoden zu statischen Methoden gemacht werden, sofern Sie keine fließende Schnittstellensyntax verwenden.
quelle