L'uso di *** Helper o *** classi Util contenenti solo metodi statici è un AntiPattern


19

Mi trovo spesso a confrontarmi con classi helper o util in Java o qualunque tipo di linguaggio. Quindi mi stavo chiedendo se si tratta di una sorta di Anti Pattern e l'esistenza di questo tipo di classi è solo una mancanza di progettazione e architettura di un software.

Spesso queste classi sono limitate usando solo metodi statici, che fanno molte cose. Ma soprattutto dipende dal contesto e dalla condizione.

La mia domanda è: qual è la tua opinione su questo tipo di classi helper / util statiche perché il vantaggio è ovviamente l'invocazione rapida usando solo il nome della classe.

E a quale tipo di astrazione eviteresti di usare questo tipo di lezioni?

A mio avviso, la parola chiave "statico" dovrebbe essere consentita solo all'interno della dichiarazione di una classe (Java) e non per i metodi. A mio avviso, usandolo in questo modo, potrebbe essere una buona alternativa e una via di mezzo per poter combinare i paradigmi procuedural e OO in Java ed evitare il mancato uso della parola chiave.

Aggiunte dovute alle risposte:

All'inizio penso che sia completamente legale poter combinare paradigmi diversi e persino usare linguaggi di script interpretati in fase di esecuzione all'interno del codice compilato di VM o VM.

La mia esperienza è che durante il processo di sviluppo di un progetto questo tipo di helpers e utils o qualunque sia il nome, crescono e crescono e vengono utilizzati in ogni angolo dimenticato della base di codice, che originariamente era progettato per essere modulare e flessibile. E a causa della mancanza di tempo per apportare rifatturazioni o ripensare al design, è solo molto peggio nel tempo.

Penso che staticdovrebbe essere rimosso da Java. Soprattutto ora dove è possibile utilizzare elementi linguistici funzionali ancora più sofisticati.



Penso che se l'helper non ha bisogno di creare un'istanza di una classe, va bene. Il problema è quando l'helper crea un'istanza di implementazione concreta di una classe e quindi non è possibile deridere quell'interfaccia. Se l'helper viene superato da un'interfaccia o da una classe astratta, penso che vada bene.
bobek,

Va bene se affermi di fare una programmazione procedurale. Non è se pretendi di fare la programmazione di oggetti (orientata).
Scoperto il

1
@Spotted: e poiché non vi è alcun valore commerciale nell'affermare di fare esclusivamente una programmazione orientata agli oggetti, basta fare sempre una programmazione a paradigma mista e fare la merda.
RemcoGerlich,

@RemcoGerlich Exactly. Il mio punto era notare che la risposta è principalmente "filosofica", in relazione a quale paradigma di programmazione la consideri.
Scoperto il

Risposte:


20

Bene, Java non ha funzioni gratuite, quindi sei costretto a metterle come funzioni statiche in una classe pro-forma. Un work-around necessario non è mai un anti-pattern, anche se potrebbe mancare di eleganza.

Successivamente, non c'è nulla di sbagliato nelle funzioni gratuite. In realtà, l'uso di funzioni libere riduce l'accoppiamento, in quanto hanno accesso solo all'interfaccia pubblica anziché a tutti i dettagli cruenti.

Naturalmente, l'uso di funzioni libere / statiche non allevia in alcun modo i pericoli di uno stato condiviso mutevole, soprattutto globale.


2
@TimothyTruckle O sono statici, o hanno un superfluo this, e richiedono che un'istanza sia opportunamente istanziata
Caleth,

6
@TimothyTruckle Perché è Java. Altre lingue possono avere funzioni gratuite senza il sovraccarico di doverle inserire in una classe solo per dichiarare che non è un metodo di istanza. E ad un certo punto, devi avere un accoppiamento stretto. Sia che è buono o non è del tutto fino a che la funzione in questione fa .
nvoigt

5
@TimothyTruckle Assolutamente, mantenere lo stato sarebbe la ragione principale per essere "non buono".
nvoigt

2
@nvoigt, penso che questa risposta sarebbe notevolmente migliorata affermando esplicitamente quel punto. La statica stateful è cattiva; gli apolidi sono buoni. Davvero abbastanza semplice.
David Arno,

3
@TimothyTruckle Non è un accoppiamento stretto. Stai descrivendo un accoppiamento libero. Un stretto accoppiamento in questo contesto implicherebbe una sorta di interdipendenza. Una dipendenza a senso unico non soddisfa tale requisito.
JimmyJames,

18

Le funzioni statiche di utilità o di aiuto non sono un modello anti se aderiscono ad alcune linee guida:

  1. Dovrebbero essere privi di effetti collaterali

  2. Sono utilizzati per comportamenti specifici dell'applicazione che non appartengono alla classe o alle classi su cui operano queste funzioni

  3. Non richiedono alcuno stato condiviso

Casi d'uso comuni:

  • Formattazione delle date in un modo specifico dell'applicazione
  • Funzioni che accettano un tipo come input e restituiscono un tipo diverso.

Ad esempio, in un'applicazione su cui ho lavorato, gli utenti conservano le voci del diario per le loro attività. Possono specificare una data di follow-up e scaricare un promemoria dell'evento. Abbiamo creato una classe di utilità statica per prendere una voce del diario e restituire il testo non elaborato per un file .ics.

Non ha richiesto alcuno stato condiviso. Non ha mutato alcuno stato e la creazione di un evento iCal era certamente specifica per l'applicazione e non apparteneva alla classe di registrazione prima nota.

Se le funzioni statiche o le classi di utilità hanno effetti collaterali o richiedono uno stato condiviso, consiglierei di rivalutare questo codice, in quanto introduce un accoppiamento che può essere difficile da deridere per i test unitari.


4

Dipenderà dal tuo approccio. Molte applicazioni sono scritte in modo più funzionale, al contrario della mentalità classica (inclusa gran parte della suite di siti in cui ti trovi).

Con questa mentalità, ci saranno molti metodi di utilità nelle classi lavoratrici che possono essere messi insieme come metodi statici. Vengono alimentati / utilizzati più vicino a oggetti semplici che contengono solo dati e vengono passati.

È un approccio valido e può funzionare molto bene, soprattutto su scala.


"può funzionare molto bene, soprattutto su scala" No. La stretta causa dell'accesso statico ti ostacolerà presto man mano che il progetto cresce.
Timothy Truckle,

@TimothyTruckle Potresti spiegare come Agent.Save (obj) è molto più strettamente accoppiato di obj.Save ()? L'agente è altrettanto in grado di ottenere un riferimento DI / IoC, anche per metodi statici come un'istanza di classe. Per riferimento Stack Exchange stesso è scritto in questo tipo di mentalità e ha ridimensionato meglio di qualsiasi altra app ASP.Net con meno risorse rispetto a qualsiasi altra di cui io sia a conoscenza.
Tracker1

"Potresti spiegare come Agent.Save (obj) è molto più strettamente accoppiato di obj.Save ()?" Questo è l'esempio sbagliato. Un esempio corretto sarebbe Agent.Save(obj)vs. agentInstance.Save(obj). Con quest'ultimo hai la possibilità di iniettare agentInstance nel codice usando. Di conseguenza è possibile iniettare qualsiasi classe figlia di Agenttroppo che permette il riutilizzo og il codice utilizzando con diversi "tipi" di Agentsenza ricompilare. Questo è un accoppiamento basso .
Timothy Truckle,

Penso che dipenda davvero da un paio di cose. È necessario lo stato, quanto deve essere flessibile ed è giusto avere uno stato globale / di istanza? Dipende da ogni sviluppatore / architetto decidere. Preferirei una base di codice più semplice rispetto all'aggiunta di complessità per scenari che rendono tutto più difficile da comprendere nel suo insieme. Una delle cose che adoro di node / js è che posso scrivere codice semplice / pulito e ancora iniettare per i test senza il codice scritto in specifiche DI / IoC.
Tracker1,

2

Personalmente penso che l'unica parte importante di tali classi di aiuto sia che siano rese private. Oltre a ciò, sono strettamente una buona idea (applicata con parsimonia).

Il modo in cui penso a questo - è se nell'implementazione di una classe (o funzione) - qualcosa di simile è utile e rende più chiara l'implementazione, come potrebbe essere male? E spesso è fondamentale definire tali classi di helper private per consentire l'integrazione e l'uso di altri algoritmi che dipendono dal fatto che i dati sono in una forma particolare.

Se etichettarlo come "aiutante" è una piccola questione di gusti personali, ma connota il fatto che aiuta l'implementazione e che non ha alcun interesse / utilizzo per un pubblico più ampio. Se è questo che ha senso, provalo!


1
Le classi di pubblica utilità potrebbero essere ancora utili. Primo esempio java.lang.Math.
amon,

1

La prevalenza delle staticclassi di aiuto si basa su un malinteso. Solo perché chiamiamo classi con solo staticmetodi "classi di utilità" non significa che non è consentito scrivere comportamenti comuni nei POJO.

static le classi di supporto sono anti pattern per tre motivi:

  1. L'accesso statico a questi metodi di supporto nasconde le dipendenze . Se queste "classi di utilità" fossero POJO, potresti immetterle in una classe dipendente come parametri del costruttore che renderebbe evidente la dipendenza per qualsiasi utente di una classe dipendente.

  2. L'accesso statico a questi metodi di supporto provoca un accoppiamento stretto . Ciò significa che il codice che utilizza i metodi di supporto è difficile da riutilizzare e (come effetto collaterale) da testare.

  3. Soprattutto se mantengono lo stato si tratta semplicemente di variabili globali . E speriamo che nessuno sostenga che le variabili globali siano buone ...

Le classi di supporto statiche fanno parte del modello STUPID anti pattern.


Lo stato globale non è correlato alla domanda, - max630

L'OP ha scritto:

Ma soprattutto dipende dal contesto e dalla condizione.

I costrutti statici statici sono stati globali.

qualsiasi tipo di codice può usarlo. - max630

Sì, di causa. E quasi tutte le applicazioni richiedono un qualche tipo di stato globale .

Ma stato globale ! = Variabile globale .

È possibile creare uno stato globale con tecniche OO tramite l' iniezione delle dipendenze Ma non è possibile evitare lo stato globale con strutture statiche statefull che sono variabili globali in fondo.


2
Lo stato globale non è correlato alla domanda, qualsiasi tipo di codice può utilizzarlo.
max630,

@ max630, per favore, vedi il mio aggiornamento
Timothy Truckle,

1
È possibile utilizzare le funzioni statiche libere senza accoppiamento. Passi intorno agli Func<Result, Args>oggetti, che sono istanziati altrove.
Caleth,

1
1) In genere penso che nascondere l'implementazione sia una buona cosa, laddove è adatto. 2) Avere tutto come argomenti / dipendenza iniettati crea anche una sorta di accoppiamento in fase di progettazione, puoi sentirlo quando ti ritrovi a dover passare molto tempo a cambiare le firme in lunghe catene di dipendenze. E come avere ogni singola funzione che richiede il tuo Logger, o altre preoccupazioni trasversali, come argomento, urgh ... (Ma sì, può rendere più difficili i test di basso livello) 3) Naturalmente le funzioni statiche dovrebbero essere apolidi.
Alex,

1
@Alex Allora lasciami dire il contrario: Un'unità è un qualsiasi gruppo di codice (compresi i metodi di "utilità" con riferimento statico) che ha lo stesso motivo per cambiare . Qualunque cosa all'interno di questa unità sono dettagli di implementazione . Qualsiasi altra cosa è una dipendenza e l'unità non dovrebbe avere accesso statico ad essa.
Timothy Truckle,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.