A classe RxJava Flowable pode legitimamente ter 460 métodos?

14

Estou apenas começando com RxJava , a implementação do Java ReactiveX (também conhecido como Rx e Extensões reativas ). Algo que realmente me chamou a atenção foi o enorme tamanho da classe Flowable do RxJava : tem 460 métodos!

Para ser justo:

  • Existem muitos métodos que estão sobrecarregados, o que aumenta significativamente o número total de métodos.

  • Talvez essa turma deva ser dividida, mas meu conhecimento e compreensão sobre o RxJava é muito limitado. As pessoas que criaram o RxJava são certamente muito espertas, e podem presumivelmente oferecer argumentos válidos para escolher criar Flowable com tantos métodos.

Por outro lado:

  • O RxJava é a implementação Java de Reactivo da Microsoft Extensões , e isso não tem nem mesmo uma classe Flowable , então este não é um caso de portar cegamente uma classe existente e implementá-la em Java.

  • [ Atualização: O ponto anterior em itálico é factualmente incorreto: a classe Observable da Microsoft, que tem mais de 400 métodos, foi usada como base para Observable e Flowable é semelhante a Observable , mas processa a contrapressão para grandes volumes de dados. Assim, a equipe do RxJava estava portando uma classe existente. Este post deveria ter desafiado o design original da classe Observable pela Microsoft em vez da classe Flowable do RxJava.]

  • O RxJava tem apenas pouco mais de 3 anos de idade, então este não é um exemplo de código mal projetado devido à falta de conhecimento sobre o bem ( SOLID ) princípios de design de classe (como foi o caso das primeiras versões do Java).

Para uma classe tão grande quanto Flowable , seu design parece inerentemente errado, mas talvez não; uma resposta a esta pergunta O que é o limite para o número de métodos de uma classe? sugeriu que a resposta é " Tenha quantos métodos forem necessários ".

Claramente, existem algumas classes que legitimamente precisam de um bom número de métodos para apoiá-los, independentemente do idioma, porque eles não se dividem facilmente em algo menor e têm um bom número de características e atributos. Por exemplo: strings, cores, células de planilhas, conjuntos de resultados de banco de dados e solicitações HTTP. Talvez algumas dúzias de métodos para representar essas coisas não pareçam irracionais.

Mas o Flowable realmente precisa de 460 métodos, ou é tão grande que é necessariamente um exemplo de design de classe ruim?

[Para ser claro: essa questão se relaciona especificamente com a classe Flowable de RxJava em vez de com os objetos God em geral.]

    
por skomisa 25.01.2018 / 08:53
fonte

3 respostas

14

TL; DL

A falta de recursos de linguagem do Java em comparação com o C #, bem como as considerações sobre a capacidade de descoberta nos fizeram colocar operadores de origem e intermediários em grandes classes.

Design

O Rx.NET original foi desenvolvido em C # 3.0, que possui dois recursos cruciais: métodos de extensão e classes parciais. O primeiro permite que você defina métodos de instância em outros tipos que, em seguida, parecem fazer parte desse tipo de destino, enquanto classes parciais permitem dividir classes grandes em vários arquivos.

Nenhum desses recursos estava presente em Java, portanto, precisávamos encontrar uma maneira conveniente de usar o RxJava.

Existem dois tipos de operadores em RxJava: representados por métodos de fábrica estáticos e como intermediários representados por métodos de instância. O primeiro poderia viver em qualquer classe e, portanto, poderia ter sido distribuído ao longo de várias classes de utilidade. Este último requer uma instância para ser trabalhada. Em conceito, todos estes poderiam ser expressos através de métodos estáticos com o upstream como primeiro parâmetro.

Na prática, no entanto, ter várias classes de entrada torna inconveniente a descoberta de recursos por novos usuários (lembre-se, o RxJava precisou trazer um novo paradigma de conceito e programação para Java) e o uso desses operadores intermediários como um pesadelo. Portanto, a equipe original criou o chamado design de API fluente: uma classe que contém todos os métodos static e instance e representa uma fonte ou um estágio de processamento por si só.

Devido à natureza de primeira classe dos erros, suporte à concorrência e natureza funcional, pode-se chegar a todos os tipos de fontes e transformações relativas ao fluxo reativo. Como a biblioteca (e conceito) evoluiu desde os dias do Rx.NET, mais e mais operadores padrão foram adicionados, o que por natureza aumentou a contagem de métodos. Isso leva a duas queixas habituais:

  • Por que existem tantos métodos?
  • Por que não existe um método X que solucione meu problema específico?
Escrever operadores reativos é uma tarefa difícil que muitas pessoas não dominam ao longo dos anos; a maioria dos usuários típicos de bibliotecas não pode criar operadores por conta própria (e muitas vezes não é realmente necessário que eles tentem). Isso significa que, de tempos em tempos, adicionamos outros operadores ao conjunto padrão. Em contraste, rejeitamos muito mais operadores por serem muito específicos ou simplesmente por uma conveniência que não pode puxar seu próprio peso.

Eu diria que o design do RxJava cresceu organicamente e não realmente ao longo de certos princípios de design, como o SOLID. É principalmente impulsionado pelo uso e sensação de sua API fluente.

Outras relações com o Rx.NET

Entrei no desenvolvimento do RxJava no final de 2013. Até onde eu sei, as primeiras versões iniciais do 0.x foram em grande parte uma reimplementação de caixa preta onde os nomes e assinaturas dos operadores Rx.NET Observable , bem como um poucas decisões arquitetônicas foram reutilizadas. Isso envolveu cerca de 20% dos operadores do Rx.NET. A principal dificuldade na época era a resolução das diferenças de linguagem e plataforma entre C # e Java. Com grande esforço, conseguimos implementar muitas operadoras sem olhar o código fonte do Rx.NET e portamos as mais complicadas.

Nesse sentido, até RxJava 0.19, nosso Observable era equivalente aos métodos de extensão IObservable do Rx.NET e seus outros métodos de extensão Observable . No entanto, o chamado problema de contrapressão apareceu e o RxJava 0.20 começou a divergir do Rx.NET em um nível de protocolo e arquitetura. As operadoras disponíveis foram extintas, muitas se tornaram conscientes da pressão de retorno e introduzimos novos tipos: Single e Completable na era 1.x, que não têm equivalentes no Rx.NET a partir de agora.

O reconhecimento de contrapressão complica consideravelmente as coisas e o 1.x Observable o recebeu como uma reflexão tardia. Nós juramos fidelidade à compatibilidade binária, então mudar o protocolo e API não era possível.

Houve outro problema com a arquitetura do Rx.NET: o cancelamento síncrono não é possível porque, para isso, é necessário que o Disposable seja retornado antes que o operador inicie a execução. No entanto, fontes como Range estavam ansiosas e não retornam até que terminem. Esse problema pode ser resolvido injetando um Disposable no Observer em vez de retornar um de subscribe() .

O RxJava 2.x foi redesenhado e reimplementado do zero ao longo destas linhas. Temos um tipo Flowable com reconhecimento de contrapressão que oferece o mesmo conjunto de operadores que Observable . Observable não suporta backpressure e é um pouco equivalente a Observable do Rx.NET. Internamente, todos os tipos reativos injetam sua alça de cancelamento em seus consumidores, permitindo que o cancelamento síncrono funcione com eficiência.

    
por 30.01.2018 / 11:06
fonte
10

Embora eu admita que não estou familiarizado com a biblioteca, dei uma olhada no Classe Flowable em questão e parece que está agindo como um hub das sortes. Em outras palavras, é uma classe destinada a validar a entrada e distribuir chamadas de acordo com o projeto.

Esta classe, portanto, não seria realmente considerada um objeto de Deus, pois um objeto de Deus é aquele que tenta fazer tudo. Isso faz muito pouco em termos de lógica. Em termos de responsabilidade única, pode-se dizer que o único trabalho da classe é delegar trabalho por toda a biblioteca.

Então, naturalmente, essa classe exigiria um método para todas as tarefas possíveis que você precisaria de uma classe Flowable neste contexto. Você vê o mesmo tipo de padrão com a biblioteca jQuery em javascript, onde a variável $ tem todas as funções e variáveis necessárias para executar chamadas na biblioteca, embora no caso do jQuery, o código não seja simplesmente delegado, mas também um bom Um pouco de lógica é executado dentro.

Acho que você deve tomar cuidado para criar uma classe como essa, mas ela tem o seu lugar, desde que o desenvolvedor se lembre de que é apenas um hub e, portanto, não é convertido lentamente em um objeto deus.

    
por 25.01.2018 / 09:06
fonte
7

O equivalente do RX do .NET a Flowable é Observável . Ele também tem todos esses métodos, mas eles são estáticos e utilizáveis como métodos de extensão . O ponto principal do RX é que a composição é escrita usando interface fluente .

Mas, para que o Java tenha uma interface fluente, é necessário que esses métodos sejam métodos de instância, já que os métodos estáticos não comporiam muito bem e não têm métodos de extensão para tornar os métodos estáticos compostos. Então, na prática, todos esses métodos podem ser feitos com métodos estáticos, desde que você não esteja usando sintaxe de interface fluente.

    
por 25.01.2018 / 09:04
fonte