Le classi di utilità sono malvagie?

Ho visto questo thread

Se una class “Utilità” è malvagia, dove inserisco il mio codice generico?

e pensato perché le classi di utilità sono malvagie?

Diciamo che ho un modello di dominio che è dozzine di classi profonde. Devo essere in grado di istanze xml-ify. Faccio un metodo toXml sul genitore? Creo una class helper MyDomainXmlUtility.toXml? Questo è un caso in cui le esigenze aziendali si estendono all’intero modello di dominio – appartiene davvero come un metodo di istanza? Che dire se ci sono un sacco di metodi ausiliari sulla funzionalità xml dell’applicazione?

    Le classi di utilità non sono esattamente malvagie, ma possono violare i principi che compongono un buon design orientato agli oggetti. In una buona progettazione orientata agli oggetti, la maggior parte delle classi dovrebbe rappresentare una singola cosa e tutti i suoi attributi e operazioni. Se stai operando su una cosa, quel metodo dovrebbe probabilmente essere un membro di quella cosa.

    Tuttavia, ci sono momentjs in cui è ansible utilizzare le classi di utilità per raggruppare più metodi: un esempio è la class java.util.Collections che fornisce un numero di utilità che possono essere utilizzate su qualsiasi raccolta Java. Questi non sono specifici per un particolare tipo di Collection, ma implementano algoritmi che possono essere usati su qualsiasi Collection.

    In realtà, ciò che devi fare è pensare al tuo design e determinare dove ha più senso mettere i metodi. Di solito, è come operazioni all’interno di una class. Tuttavia, a volte, è davvero una class di utilità. Quando si utilizza una class di utilità, tuttavia, non solo inserire metodi casuali in essa, ma organizzare i metodi in base allo scopo e alla funzionalità.

    Penso che il consenso generale sia che le classi di utilità non sono di per sé malvagie. Hai solo bisogno di usarli con giudizio:

    • Progettare i metodi di utilità statici per essere generali e riutilizzabili. Assicurati che siano apolidi; cioè senza variabili statiche.

    • Se disponi di molti metodi di utilità, suddividili in classi in modo tale che sia facile per gli sviluppatori trovarli.

    • Non utilizzare classi di utilità in cui i metodi statici o di istanza in una class di dominio potrebbero essere una soluzione migliore. Ad esempio, considera se i metodi in una class base astratta o in una class helper istanziabile siano una soluzione migliore.

    • Per Java 8 in poi, i “metodi predefiniti” in un’interfaccia potrebbero essere un’opzione migliore rispetto alle classi di utilità.


    L’altro modo di guardare questa domanda è di osservare che nella domanda citata, “Se le classi di utilità sono” malvagie “” è un argomento di paglia. È come me che chiedo:

    “Se i maiali possono volare, dovrei portare un ombrello?”.

    Nella domanda di cui sopra, in realtà non sto dicendo che i maiali possono volare … o che sono d’accordo con la proposta di poter volare.

    Le tipiche affermazioni “xyz is evil” sono dispositivi retorici che hanno lo scopo di farti pensare ponendo un punto di vista estremo. Sono raramente (se mai) intesi come affermazioni di fatto letterale.

    Le classi di utilità sono problematiche perché non riescono a raggruppare le responsabilità con i dati che le supportano.

    Sono comunque estremamente utili e li costruisco tutto il tempo come strutture permanenti o come trampolini di lancio durante un refactoring più completo.

    Dalle classi di utilità di prospettiva di Clean Code violano la Single Responsibility e il Open-Closed Principle. Hanno molti motivi per cambiare e non sono estensibili. Dovrebbero davvero esistere solo durante il refactoring come cruft intermedio.

    Suppongo che inizi a diventare malvagio quando

    1) Diventa troppo grande (basta raggrupparli in categorie significative in questo caso).
    2) Sono presenti metodi che non dovrebbero essere metodi statici

    Ma fintanto che queste condizioni non sono soddisfatte, penso che siano molto utili.

    Le classi di utilità sono cattive perché significano che sei troppo pigro per trovare un nome migliore per la class 🙂

    Detto questo, sono pigro. A volte hai solo bisogno di portare a termine il lavoro e la tua mente è vuota … questo è il momento in cui le classi “Utility” iniziano a insinuarsi.

    È molto facile etichettare qualcosa di utile semplicemente perché il progettista non può pensare a un posto appropriato dove inserire il codice. Ci sono spesso poche vere “utilità”.

    Come regola generale, di solito tengo il codice nel pacchetto in cui viene utilizzato per la prima volta, quindi riadattato solo in un posto più generico se trovo che in seguito è necessario in altre parti. L’unica eccezione è se ho già un pacchetto che esegue funzionalità simili / correlate, e il codice si adatta meglio lì.

    Non sono del tutto d’accordo sul fatto che le classi di utilità siano malvagie.

    Mentre una class di utilità può violare i principi di OO in qualche modo, non sempre sono cattivi.

    Ad esempio, immagina di volere una funzione che pulirà una stringa di tutte le sottostringhe corrispondenti al valore x.

    stl c ++ (come ora) non supporta direttamente questo.

    Potresti creare un’estensione polimorfa di std :: string.

    Ma il problema è, vuoi davvero OGNI stringa che usi nel tuo progetto per essere la tua class di stringa estesa?

    Ci sono momentjs in cui OO non ha molto senso, e questo è uno di questi. Vogliamo che il nostro programma sia compatibile con altri programmi, quindi continueremo con std :: string e creeremo una class StringUtil_ (o qualcosa).

    Direi che è il massimo se usi solo un util per class. Direi che è sciocco avere un utile per tutte le classi o molti programmi di utilità per una class.

    Le classi di utilità contenenti metodi stateless stateless possono essere utili. Questi sono spesso molto facili da testare l’unità.

    Con Java 8 è ansible utilizzare i metodi statici nelle interfacce … risolto il problema.

    Ripensando a questa domanda ora, direi che i metodi di estensione C # distruggono completamente la necessità di classi di utilità. Ma non tutte le lingue hanno un costrutto così geniale.

    Hai anche JavaScript, dove puoi semplicemente aggiungere una nuova funzione direttamente all’object esistente.

    Ma non sono sicuro che ci sia un modo elegante per risolvere questo problema in una lingua più vecchia come il C ++ …

    Un buon codice OO è piuttosto difficile da scrivere ed è difficile da trovare poiché scrivere Good OO richiede molto più tempo / conoscenza che scrivere un codice funzionale decente.

    E quando hai un budget limitato, il tuo capo non è sempre felice di vedere che hai trascorso l’intera giornata a scrivere un sacco di lezioni …

    La maggior parte delle classi Util sono cattive perché:

    1. Ampliano l’ambito dei metodi. Rendono pubblico il codice che altrimenti sarebbe privato. Se il metodo util è stabile (ovvero non necessita di aggiornamenti) è meglio, a mio parere, copiare e incollare metodi di helper privati ​​piuttosto che esporli come API e rendere più difficile capire quale sia il punto di accesso pubblico a un componente (tu mantenere un albero strutturato chiamato gerarchia con un genitore per metodo che è più facile separare mentalmente in componenti che è più difficile quando si hanno metodi chiamati da più metodi padre).
    2. Risultano in codice morto. I metodi Util nel tempo diventano inutilizzati man mano che la tua app si evolve e finisci con il codice inutilizzato che inquina il tuo codice base. Se fosse rimasto privato, il tuo compilatore ti direbbe che il metodo non è utilizzato e potresti semplicemente rimuoverlo (il codice migliore non è affatto un codice).

    Esistono alcune analogie con le librerie statiche e dinamiche.

    Regola del pollice

    Puoi guardare questo problema da due punti di vista:

    • Nel complesso *Util metodi di *Util sono spesso un suggerimento di progettazione di codice errato o convenzione di denominazione lenta.
    • È una soluzione di design legittima per funzionalità stateless riutilizzabili tra domini. Si noti che per quasi tutti i problemi comuni esistono soluzioni esistenti.

    Esempio 1. Uso corretto di classi / moduli util . Esempio di libreria esterna

    Supponiamo che tu stia scrivendo un’applicazione che gestisce i prestiti e le carte di credito. I dati di questi moduli sono esposti tramite servizi Web in formato json . In teoria puoi convertire manualmente gli oggetti in stringhe che saranno in json , ma ciò reinventerebbe la ruota. La soluzione corretta sarebbe quella di includere in entrambi i moduli la libreria esterna utilizzata per convertire gli oggetti java nel formato desiderato. (nell’immagine di esempio ho mostrato gson )

    inserisci la descrizione dell'immagine qui


    Esempio 2. Uso corretto di classi / moduli util . Scrivere il proprio util senza scuse per gli altri membri del team

    Come caso d’uso, supponiamo di dover eseguire alcuni calcoli in due moduli di applicazione, ma entrambi devono sapere quando ci sono giorni festivi in ​​Polonia. In teoria è ansible eseguire questi calcoli all’interno dei moduli, ma sarebbe meglio estrarre questa funzionalità in un modulo separato.

    Ecco piccoli dettagli, ma importanti. La class / modulo che hai scritto non si chiama HolidayUtil , ma PolishHolidayCalculator . Funzionalmente è una class util , ma siamo riusciti a evitare parole generiche.

    inserisci la descrizione dell'immagine qui

    Le classi di utilità non sono sempre malvagie. Ma dovrebbero contenere solo i metodi che sono comuni a un’ampia gamma di funzionalità. Se esistono metodi utilizzabili solo in un numero limitato di classi, prendere in considerazione la creazione di una class astratta come genitore comune e inserire i metodi al suo interno.

    Quando non riesco ad aggiungere un metodo a una class (ad esempio, Account è bloccato rispetto alle modifiche di Jr. Developers), aggiungo solo alcuni metodi statici alla mia class di utilità in questo modo:

     public static int method01_Account(Object o, String... args) { Account acc = (Account)o; ... return acc.getInt(); }