O uso das classes *** Helper ou *** Util contendo apenas métodos estáticos é um AntiPattern

19

Sou frequentemente confrontado com classes auxiliares ou util em Java ou qualquer tipo de linguagem. Então, eu estava me perguntando se isso é algum tipo de anti-padrão e a existência desse tipo de classe é apenas uma falta de falta no design e na arquitetura de um software.

Frequentemente, essas classes são confinadas usando apenas métodos estáticos, que fazem muitas coisas. Mas, na maioria das vezes, é de fato dependente do contexto e cheio de estado.

Minha pergunta é: qual é a sua opinião sobre esse tipo de classes auxiliares / utilitárias estáticas, porque a vantagem é, obviamente, a chamada rápida usando apenas o nome da classe.

E em que tipo de nível de abstração você evitaria usar esse tipo de classe?

Na minha opinião, a palavra-chave "estático" deve ser permitida apenas dentro da declaração de uma classe (Java) e não para métodos. Na minha opinião, é possível usá-lo dessa maneira, uma boa alternativa e meio caminho para combinar os Paradigmas de Procedimentos e OO em Java e evitar o uso incorreto da palavra-chave.

Acréscimos devido às respostas:

No começo, eu acho que é completamente legal poder combinar paradigmas diferentes e até mesmo usar linguagens de script interpretadas em tempo de execução no código compilado por máquina ou vm.

Minha experiência é que, durante o processo de desenvolvimento de um projeto, esse tipo de ajudante e utilitário, ou qualquer que seja o nome, está crescendo e crescendo e usado em todos os cantos esquecidos da base de código, que foi originalmente projetada para ser modular e flexível. E devido à falta de tempo para fazer refatorações ou pensar novamente no design, você só piora com o tempo.

Eu acho que staticdeve ser removido do Java. Especialmente agora, onde é possível usar elementos de linguagem funcional ainda mais sofisticados.

Diversidade
fonte
Acho que se o ajudante não precisar instanciar uma aula, tudo bem. O problema é quando o auxiliar instancia uma implementação concreta de uma classe e você não consegue zombar dessa interface. Se o ajudante receber uma interface ou uma classe abstrata, acho que está tudo bem.
Bobek
Tudo bem se você afirma fazer programação processual. Não é se você afirma fazer programação de objetos (orientados).
manchado
1
@ Spotted: e como não há valor comercial em afirmar que você faz apenas programação orientada a objetos, sempre faça programação de paradigma misto e faça merda.
RemcoGerlich
@RemcoGerlich Exatamente. Meu objetivo foi observar que a resposta é principalmente "filosófica", relacionada a partir do paradigma de programação que você considera.
manchado

Respostas:

20

Bem, Java não possui funções livres, portanto você é forçado a colocá-las como funções estáticas em alguma classe pró-forma. Uma solução alternativa necessária nunca é um antipadrão, embora possa faltar elegância.

Em seguida, não há nada errado com as funções livres. Na verdade, o uso de funções livres reduz o acoplamento, pois eles só têm acesso à interface pública em vez de todos os detalhes sangrentos.

Obviamente, o uso de funções estáticas / livres não diminui de maneira alguma os perigos de um estado compartilhado mutável, especialmente global.

Desduplicador
fonte
2
@TimothyTruckle Ou eles são estáticos, ou eles têm um supérfluo this, e exigir algo ser instanciado de forma adequada
Caleth
6
@TimothyTruckle Porque é Java. Outros idiomas podem ter funções livres sem a sobrecarga de colocá-las em uma classe apenas para declarar que não é um método de instância. E em algum momento, você precisa ter um acoplamento apertado. Se isso é bom ou não, depende inteiramente do que a função em questão faz .
Nvoigt 19/07/19
5
@ TimothyTruckle Absolutamente, manter o estado seria o principal motivo para não ser "bom".
Nvoigt 19/07/19
2
@ Nvoigt, eu sinto que esta resposta seria muito melhorada, afirmando explicitamente esse ponto. A estática estatal é ruim; os apátridas são bons. Realmente bastante simples.
David Arno
3
@ TimothyTruckle Isso não é acoplamento apertado. Você está descrevendo o acoplamento solto. Um forte acoplamento nesse contexto implicaria algum tipo de interdependência. Uma dependência unidirecional não atende a esse requisito.
21718 JimmyJames
18

Utilitário estático ou funções auxiliares não são um antipadrão se seguirem algumas diretrizes:

  1. Eles devem estar livres de efeitos colaterais

  2. Eles são usados ​​para comportamentos específicos de aplicativos que não pertencem à classe ou classes nas quais essas funções operam

  3. Eles não exigem nenhum estado compartilhado

Casos de uso comuns:

  • Formatando datas de uma maneira específica do aplicativo
  • Funções que recebem um tipo como entrada e retornam um tipo diferente.

Por exemplo, em um aplicativo em que trabalhei, os usuários mantêm registros de diário para suas atividades. Eles podem especificar uma data de acompanhamento e baixar um lembrete de evento. Criamos uma classe de utilitário estático para obter uma entrada no diário e retornar o texto bruto para um arquivo .ics.

Não exigiu nenhum estado compartilhado. Ele não modificou nenhum estado e a criação de um evento do iCal certamente foi específica do aplicativo e não pertence à classe de entrada no diário.

Se as funções estáticas ou as classes de utilitário tiverem efeitos colaterais ou exigirem um estado compartilhado, recomendo reavaliar esse código, pois ele introduz acoplamentos que podem ser difíceis de zombar para testes de unidade.

Greg Burghardt
fonte
4

Vai depender da sua abordagem. Muitos aplicativos são escritos com uma mentalidade mais funcional, em oposição à mentalidade clássica (incluindo grande parte do conjunto de sites em que você está).

Com essa mentalidade, haverá muitos métodos utilitários nas classes de trabalho que podem ser agrupados como métodos estáticos. Eles são alimentados / usados ​​mais perto de objetos simples que apenas retêm dados e são transmitidos.

É uma abordagem válida e pode funcionar muito bem, especialmente em escala.

Tracker1
fonte
"pode ​​funcionar muito bem, especialmente em escala" Não. A causa do acoplamento apertado pelo acesso estático em breve impedirá que o projeto cresça.
Timothy Truckle
@TimothyTruckle Você poderia explicar como o Agent.Save (obj) é muito mais vinculado do que o obj.Save ()? O agente é tão capaz de obter uma referência de DI / IoC, mesmo para métodos estáticos quanto uma instância de classe. Para referência, o Stack Exchange em si está escrito nesse tipo de mentalidade e foi dimensionado melhor do que qualquer outro aplicativo ASP.Net com menos recursos do que qualquer outro que eu conheço.
precisa
"Você poderia explicar como o Agent.Save (obj) é muito mais fortemente associado que o obj.Save ()?" Este é o exemplo errado. Um exemplo seria correcto Agent.Save(obj)vs agentInstance.Save(obj). Com o último, você tem a possibilidade de injetar agentInstance no código de uso. Em conseqüência, você pode injetar qualquer classe criança de Agenttambém que permite a reutilização og o código usando com diferentes "tipos" de Agentsem recompilar. Isso é baixo acoplamento .
21718 Timothy Truckle
Eu acho que realmente se resume a algumas coisas. O estado é necessário, quão flexível ele precisa ser e é aceitável ter um estado global / instância? Cabe a cada desenvolvedor / arquiteto decidir. Eu preferiria uma base de código mais simples do que adicionar complexidade para cenários que tornam tudo mais difícil de entender como um todo. Uma das coisas que eu amo no nó / js é que eu posso escrever código simples / limpo e ainda injetar para teste sem o código gravado nas especificações de DI / IoC.
precisa
2

Pessoalmente, acho que a única parte importante dessas classes auxiliares é que elas são tornadas privadas. Além disso - são estritamente uma boa ideia (aplicada com moderação).

A maneira como penso sobre isso - é se, ao implementar uma classe (ou função) - algo assim é útil e torna essa implementação mais clara, como isso pode ser ruim? E, muitas vezes, é fundamental definir essas classes auxiliares privadas para permitir a integração e o uso de outros algoritmos que dependem de os dados estarem em uma forma específica.

O fato de rotulá-lo de 'ajudante' é uma pequena questão de gosto pessoal, mas indica que ajuda na implementação e não é de interesse / uso para um público mais amplo. Se é isso que faz sentido - vá em frente!

Lewis Pringle
fonte
1
As classes de utilidade pública ainda podem ser úteis. Excelente exemplo: java.lang.Math.
amon
1

A prevalência de staticclasses auxiliares é baseada em um equívoco. Só porque chamamos classes com apenas staticmétodos "classes utilitárias" não significa que não é permitido escrever comportamento comum em POJOs.

static As classes auxiliares são antipadrão por três razões:

  1. O acesso estático a esses métodos auxiliares oculta dependências . Se essas "classes utilitárias" fossem POJOs, você poderia injetá-las int a uma classe dependente como parâmetros construtores, o que tornaria a dependência óbvia para qualquer usuário de uma classe dependente.

  2. O acesso estático a esses métodos auxiliares causa um acoplamento apertado . Isso significa que o código que utiliza os métodos auxiliares é difícil de reutilizar e (como efeito colateral) difícil de testar.

  3. Especialmente se eles mantiverem estado, essas são apenas variáveis ​​globais . E espero que ninguém discuta que variáveis ​​globais são boas ...

As classes auxiliares estáticas fazem parte do anti-padrão do código STUPID .


O estado global não está relacionado à pergunta, - max630

O OP escreveu:

Mas, na maioria das vezes, é de fato dependente do contexto e cheio de estado.

Construções estáticas statefull são estados globais.

qualquer tipo de código pode usá-lo. - max630

Sim, de causa. E quase todos os aplicativos precisam de algum tipo de estado global .

Mas estado global ! = Variável global .

Você pode criar estado global com técnicas de OO via injeção de dependência. Mas não é possível evitar o estado global com estruturas estáticas cheias de estado, que são variáveis ​​globais na parte inferior.

Timothy Truckle
fonte
2
O estado global não está relacionado à questão, qualquer tipo de código pode usá-lo.
max630
@ max630 consulte o meu update
Timothy Truckle
1
Você pode usar funções livres de estática sem acoplamento. Você passa Func<Result, Args>objetos que são instanciados em outro lugar.
Caleth
1
1) Em geral, acho que ocultar a implementação é uma coisa boa, onde é adequado. 2) Ter tudo como argumento / injeção de dependência também cria um tipo de acoplamento em tempo de design, você pode sentir isso quando se vê gastando muito tempo alterando assinaturas em longas cadeias de dependência. E como ter todas as funções que exigem seu Logger, ou outras preocupações transversais, como argumento, urgente ... (Mas sim, isso pode dificultar o teste de nível inferior) 3) É claro que as funções estáticas devem ser sem estado.
21718 Alex
1
@ Alex Então deixe-me dizer o contrário: Uma unidade é qualquer monte de código (incluindo métodos de "utilidade" referenciados estáticos) que tem o mesmo motivo para mudar . Qualquer coisa dentro desta unidade é detalhe de implementação . Qualquer outra coisa é uma dependência e a unidade não deve ter acesso estático a ela.
Timothy Truckle