Domare le classi delle "utility function"

14

Nel nostro codebase Java continuo a vedere il seguente schema:

/**
 This is a stateless utility class
 that groups useful foo-related operations, often with side effects.
*/
public class FooUtil {
  public int foo(...) {...}
  public void bar(...) {...}
}

/**
 This class does applied foo-related things.
*/
class FooSomething {
  int DoBusinessWithFoo(FooUtil fooUtil, ...) {
    if (fooUtil.foo(...)) fooUtil.bar(...);
  }
}

Ciò che mi dà fastidio è che devo passare un'istanza di FooUtil ovunque, perché test.

  • Non riesco a rendere statici i metodi di FooUtil , perché non sarò in grado di prenderli in giro per testare sia FooUtil che le sue classi client.
  • Non riesco a creare un'istanza di FooUtil nel luogo di consumo con new , anche perché non potrò prenderlo in giro per testare.

Suppongo che la mia migliore scommessa sia usare l'iniezione (e lo faccio io), ma aggiunge il suo insieme di problemi. Inoltre, il passaggio di più istanze utili aumenta la dimensione degli elenchi dei parametri del metodo.

C'è un modo per gestire meglio questo che non vedo?

Aggiornamento: poiché le classi di utilità sono stateless, potrei probabilmente aggiungere un membro statico INSTANCE o un metodo getInstance() statico, pur mantenendo la possibilità di aggiornare il campo statico sottostante di la classe per i test. Non sembra neanche troppo pulito.

    
posta 9000 05.01.2015 - 21:47
fonte

2 risposte

13

Prima di tutto lasciatemi dire che ci sono diversi approcci di programmazione per diversi problemi. Per il mio lavoro preferisco un design OO strong per l'orginazation e la manutenzione e la mia risposta riflette questo. Un approccio funzionale avrebbe una risposta molto diversa.

Tendo a scoprire che la maggior parte delle classi di utilità vengono create perché l'oggetto su cui i metodi dovrebbero essere attivi non esiste. Questo deriva quasi sempre da uno dei due casi, o qualcuno sta passando delle collezioni o qualcuno sta passando dei bean (questi sono spesso chiamati POJO ma credo che un gruppo di setter e getter sia meglio descritto come un bean).

Quando hai una collezione che stai passando, deve esserci un codice che vuole far parte di quella raccolta, e questo finisce per essere cosparso nel tuo sistema e alla fine raccolto in classi di utilità.

Il mio suggerimento è quello di avvolgere le tue raccolte (o fagioli) con altri dati strettamente associati in nuove classi e inserire il codice "utilità" in oggetti reali.

Potresti estendere la raccolta e aggiungere i metodi lì, ma perdi il controllo dello stato dell'oggetto in quel modo: ti suggerisco di incapsularla completamente e di esporre solo i metodi della logica di business che sono necessari (Pensa "Non chiedere i tuoi oggetti per i loro dati, danno ai tuoi oggetti comandi e lascia che agiscano sui loro dati privati ", un inquilino OO importante e uno che, quando ci pensi, richiede l'approccio che sto suggerendo qui).

Questo "involucro" è utile per qualsiasi oggetto di libreria di uso generico che contiene dati ma non è possibile aggiungere codice a - Inserire il codice con i dati che manipola è un concetto importante nella progettazione OO.

Ci sono molti stili di codifica in cui questo concetto di wrapping sarebbe una cattiva idea - chi vuole scrivere un'intera classe solo implementando uno script o un test di una sola volta? Ma penso che l'incapsulamento di tutti i tipi / collezioni di base che non puoi aggiungere codice a te stesso sia obbligatorio per OO.

    
risposta data 06.01.2015 - 20:38
fonte
1

Potresti usare un pattern Provider e iniettare FooSomething con un oggetto FooUtilProvider (potrebbe essere in un costruttore, solo una volta).

FooUtilProvider avrebbe solo un metodo: FooUtils get(); . La classe utilizzerà quindi qualsiasi istanza di FooUtils che fornisce.

Ora è a un passo dall'utilizzo di un framework Dipendenza Iniezione, quando dovresti solo collegare il gettone DI due volte: per il codice di produzione e per la tua suite di test. Basta collegare FooUtils all'interfaccia a RealFooUtils o MockedFooUtils , e il resto avviene in modo automatico. Ogni oggetto che dipende da FooUtilsProvider ottiene la versione corretta di FooUtils .

    
risposta data 07.01.2015 - 01:43
fonte

Leggi altre domande sui tag