La classe RxJava Flowable può legittimamente avere 460 metodi?

14

Ho appena iniziato con RxJava , l'implementazione di Java di ReactiveX (noto anche come Rx e Estensioni reattive ). Qualcosa che mi ha veramente colpito è stata la dimensione enorme della classe Flowable di RxJava : ha 460 metodi!

Per essere onesti:

  • Esistono molti metodi che sono sovraccarichi, il che riduce significativamente il numero totale di metodi.

  • Forse questa classe dovrebbe essere suddivisa, ma la mia conoscenza e comprensione di RxJava è molto limitata. Le persone che hanno creato RxJava sono sicuramente molto intelligenti e possono presumibilmente offrire argomenti validi per la scelta di creare Flowable con così tanti metodi.

D'altra parte:

  • RxJava è l'implementazione Java di Reactive di Microsoft Estensioni e non ha nemmeno una classe Flowable , quindi non è un caso di porting di una classe esistente e implementazione in Java.

  • [ Aggiornamento: il punto in corsivo precedente è effettivamente errato: la classe Observable di Microsoft, che ha oltre 400 metodi, è stata utilizzata come base per RxJava < La classe strong> Observable e Flowable è simile a Observable ma gestisce la contropressione per grandi volumi di dati. Quindi il team di RxJava stava porting una classe esistente. Questo post avrebbe dovuto sfidare il design originale della classe Observable di Microsoft piuttosto che la Flowable classe di RxJava.

  • RxJava ha solo poco più di 3 anni, quindi questo non è un esempio di codice progettato male per la mancanza di conoscenza del bene ( SOLID ) principi di progettazione della classe (come nel caso delle prime versioni di Java).

Per una classe grande quanto Flowable il suo design sembra intrinsecamente sbagliato, ma forse no; una risposta a questa domanda SE Che cos'è il limite al numero di un metodo di classe? ha suggerito che la risposta è " Hai tutti i metodi di cui hai bisogno ".

Chiaramente ci sono alcune classi che hanno bisogno legittimo di un buon numero di metodi per supportarle indipendentemente dalla lingua, perché non si suddividono facilmente in qualcosa di più piccolo e hanno un buon numero di caratteristiche e attributi. Ad esempio: stringhe, colori, celle del foglio di calcolo, set di risultati del database e richieste HTTP. Avere forse qualche dozzina di metodi per le classi per rappresentare quelle cose non sembra irragionevole.

Ma Flowable ha veramente bisogno di 460 metodi, o è così grande che è necessariamente un esempio di design di classe scadente?

[Per essere chiari: questa domanda riguarda specificamente la classe Flowable di RxJava piuttosto che gli oggetti di Dio in generale.]

    
posta skomisa 25.01.2018 - 08:53
fonte

3 risposte

14

TL; DL

La mancanza di funzionalità linguistiche di Java rispetto a C # e le considerazioni sulla rilevabilità ci hanno portato a collocare operatori sorgente e intermedi in classi di grandi dimensioni.

design

L'originale Rx.NET è stato sviluppato in C # 3.0 che ha due caratteristiche cruciali: i metodi di estensione e le classi parziali. Il primo consente di definire i metodi di istanza su altri tipi che sembrano essere parte di quel tipo di destinazione, mentre le classi parziali consentono di dividere grandi classi in più file.

Nessuna di queste funzionalità era o è presente in Java, quindi, abbiamo dovuto trovare un modo per utilizzare RxJava in modo appropriato.

Esistono due tipi di operatori in RxJava: source-like rappresentato da metodi factory statici e intermedi come rappresentati da metodi di istanza. Il primo potrebbe vivere in qualsiasi classe e quindi potrebbero essere stati distribuiti su più classi di utilità. Quest'ultimo richiede un'istanza su cui lavorare. In teoria, tutti questi possono essere espressi tramite metodi statici con upstream come primo parametro.

In pratica, tuttavia, avere più classi di accesso rende scomoda la scoperta di nuove funzionalità da parte dei nuovi utenti (ricorda, RxJava ha dovuto introdurre un nuovo concetto e paradigma di programmazione in Java) e l'utilizzo di questi operatori intermedi un po 'da incubo. Pertanto, il team originale ha ideato il cosiddetto design fluente dell'API: una classe che contiene tutti i metodi statici e di istanza e rappresenta una fase di origine o di elaborazione tutto da sola.

A causa della natura di errori di prima classe, del supporto della concorrenza e della natura funzionale, è possibile inventare ogni sorta di fonti e trasformazioni riguardanti il flusso reattivo. Poiché la libreria (e il concetto) si sono evoluti dai tempi di Rx.NET, sono stati aggiunti sempre più operatori standard, che per loro natura hanno aumentato il numero di metodi. Questo ha portato a due soliti reclami:

  • Perché ci sono così tanti metodi?
  • Perché non esiste un metodo X che risolva il mio problema particolare?

Scrivere operatori reattivi è un compito difficile che molte persone non hanno imparato nel corso degli anni; la maggior parte degli utenti delle librerie non è in grado di creare autonomamente gli operatori (e spesso non è necessario per loro provarli). Ciò significa che, di volta in volta, aggiungere ulteriori operatori al set standard. Al contrario, abbiamo rifiutato molti più operatori perché troppo specifici o semplicemente una comodità che non può tirare il suo peso.

Direi che il design di RxJava è cresciuto in modo organico e non proprio lungo alcuni principi di progettazione come SOLID. È principalmente guidato dall'utilizzo e dalla sensazione della sua fluente API.

Altre relazioni con Rx.NET

Sono entrato nello sviluppo di RxJava alla fine del 2013. Per quanto posso dire, le iniziali versioni 0.x iniziali erano in gran parte una reimplementazione black-box in cui i nomi e le firme degli operatori di Rx.NET Observable e un alcune decisioni architettoniche sono state riutilizzate. Ciò ha coinvolto circa il 20% degli operatori Rx.NET. La principale difficoltà allora era la risoluzione delle differenze linguistiche e di piattaforma tra C # e Java. Con grande sforzo, siamo riusciti a implementare molti operatori senza guardare il codice sorgente di Rx.NET e abbiamo portato quelli più complicati.

In questo senso, fino a RxJava 0.19, il nostro Observable era equivalente al IObservable di Rx.NET e ai suoi metodi di estensione Observable companion. Tuttavia, il cosiddetto problema di contropressione è apparso e RxJava 0.20 ha iniziato a divergere da Rx.NET a livello di protocollo e architettura. Gli operatori disponibili sono stati estinti, molti sono diventati consapevoli della retropressione e abbiamo introdotto nuovi tipi: Single e Completable nell'era 1.x, che al momento non hanno controparti in Rx.NET.

La consapevolezza della contropressione complica notevolmente le cose e il 1.x% diObservable lo ha ricevuto in seguito. Abbiamo giurato fedeltà alla compatibilità binaria, quindi la modifica del protocollo e dell'API non era possibile.

C'era un altro problema con l'architettura di Rx.NET: l'annullamento sincrono non è possibile perché per farlo è necessario restituire Disposable prima che l'operatore inizi l'esecuzione. Tuttavia, fonti come Range erano ansiose e non ritornano finché non finiscono. Questo problema può essere risolto iniettando un Disposable in Observer invece di restituirne uno da subscribe() .

RxJava 2.x era riprogettato e reimplementato da zero seguendo queste linee. Abbiamo un tipo Flowable separato, di tipo backpressure-aware che offre lo stesso set di operatori di Observable . Observable non supporta la contropressione ed è in qualche modo equivalente a Observable di Rx.NET. Internamente, tutti i tipi reattivi iniettano il loro handle di annullamento ai propri consumatori, consentendo all'eliminazione sincrona di funzionare in modo efficiente.

    
risposta data 30.01.2018 - 11:06
fonte
10

Mentre ammetto che non conosco la libreria, ho dato un'occhiata a classe Flowable in questione e sembra che si stia comportando come un hub di sorta. In altre parole, è una classe pensata per convalidare l'input e distribuire le chiamate di conseguenza nel progetto.

Questa classe quindi non sarebbe veramente considerata un oggetto di Dio come un oggetto di Dio è uno che tenta di fare tutto. Questo fa molto poco in termini di logica. In termini di responsabilità unica, si può dire che l'unico lavoro della classe sia delegare il lavoro in tutta la biblioteca.

Quindi, naturalmente, una classe di questo tipo richiederebbe un metodo per ogni possibile attività di una classe Flowable in questo contesto. Si vede lo stesso tipo di pattern con la libreria jQuery in javascript, dove la variabile $ ha tutte le funzioni e le variabili necessarie per eseguire chiamate sulla libreria, anche se nel caso di jQuery, il codice non è semplicemente delegato ma anche un buon un po 'di logica viene eseguita all'interno.

Penso che dovresti fare attenzione a creare una classe come questa, ma ha il suo posto fintanto che lo sviluppatore ricorda che è solo un hub, e quindi non si converte lentamente in un oggetto dio.

    
risposta data 25.01.2018 - 09:06
fonte
7

L'RX di .NET equivale a Flowable è osservabile . Ha anche tutti questi metodi, ma sono statici e utilizzabili come metodi di estensione . Il punto principale di RX è che la composizione è scritta usando l'interfaccia fluente .

Ma per Java avere un'interfaccia fluente, ha bisogno che quei metodi siano metodi di istanza, poiché i metodi statici non compongono bene e non ha metodi di estensione per rendere componibili i metodi statici. Quindi, in pratica, tutti questi metodi potrebbero essere fatti come metodi statici, a patto che non stiate usando una sintassi di interfaccia fluente.

    
risposta data 25.01.2018 - 09:04
fonte

Leggi altre domande sui tag