Come restituire gli avvisi insieme al risultato del calcolo al chiamante di un metodo Java? [duplicare]

25

Ho una classe, chiamiamola Calculator , con un metodo come questo:

public double[] performCalculation(double[] someInData)

Questo metodo può generare un numero di avvisi non fatali (rappresentati come una serie di stringhe con nomi di avvertenza, come InDataOutOfBounds ). Qual è il modo più elegante e javaque per restituire questi avvisi al chiamante? Vedo alcune possibilità, nessuna delle quali mi piace particolarmente:

  1. Restituisce un'istanza di una classe personalizzata contenente sia il risultato del calcolo sia l'array di warings.
  2. Consenti a performCalculation di accettare un argomento aggiuntivo String[] warnings che viene compilato con gli avvertimenti. Questo potrebbe essere un no go, in quanto sembra che non posso ridimensionare l'array passato, e non c'è modo di sapere in anticipo quanti avvertimenti ci saranno.
  3. Mantieni performCalculation così com'è, ma aggiungi un metodo String[] getWarnings() a Calculator , restituendo gli avvertimenti dall'ultimo calcolo.

EDIT: sono a conoscenza di questa domanda. Direi che questo non è un duplicato, poiché la risposta più alta a questa domanda dice che la risposta dipende dalla situazione. Questo è un caso più specifico, che può ottenere una risposta più specifica.

    
posta Anders 13.04.2016 - 10:43
fonte

7 risposte

50
  1. Sì. Fai questo. È Java, non temere le lezioni. Ecco a cosa servono le classi.

  2. Davvero male. È leggermente meno male con un Collection invece di un array, ma stai ancora chiedendo una semantica ad-hoc mal specificata che non sarà mai usata correttamente.

  3. Può funzionare se sei assolutamente, positivamente sicuro che non userai mai, mai, Calculator tranne in una situazione a flusso singolo di controllo. Sfortunatamente, parallelizzare le cose è una delle modifiche più comuni richieste dopo il fatto.

risposta data 13.04.2016 - 10:48
fonte
26

La tua opzione 1 è la soluzione migliore, penso. Restituisce tutti i risultati, inclusi gli avvertimenti, in un oggetto personalizzato. Non stai mantenendo lo stato all'interno dell'oggetto che esegue la chiamata al metodo e ciò significa che non devi preoccuparti di conservare e gestire il tuo stato (ad esempio in uno scenario con thread o anche chiamare il metodo due volte di seguito e inavvertitamente perdere i risultati) .

Non aver paura di costruire oggetti personalizzati per questo genere di cose. Oltre a contenere i campi, puoi anche aggiungere metodi a questo oggetto per legare insieme queste informazioni e generare rapporti sullo stato, ad es. %codice%).

Probabilmente scoprirai che più funzionalità rientrano in questo oggetto di ritorno. Se sto facendo lo stesso (ad esempio in Scala), avvio spesso restituendo una tupla (serie di risultati legati insieme) e molto rapidamente la converto in una nuova classe con funzionalità aggiuntive)

    
risposta data 13.04.2016 - 10:54
fonte
16

Una quarta opzione, piuttosto che passare in una String[] warnings (o più - flessibilmente una% mutabile% co_de) come suggerisci in # 2, sarebbe quella di passare in un listener di callback o oggetto che riceve e visualizza gli avvertimenti. Per calcoli veloci, questo è molto probabilmente eccessivo, ma potrebbe essere un modo per andare se il calcolo è di lunga durata o altrimenti deve iniziare a mostrare notifiche asincrone. Se si utilizza questo modello, è possibile che si desideri fornire un'implementazione di listener non operativo e un'implementazione di listener di aggregazione per un'ispezione successiva e considerare inoltre di rendere l'ascoltatore nullable o facoltativo (tramite overload).

public interface WarningListener {
  void warn(String warning);
  void showProgress(int percentage);
}

public Result performCalculation(Input input, WarningListener listener) { ... }

Tuttavia, a meno che non ci fosse una ragione specifica per fare in modo che il calcolo richiamasse presto, andrei con l'opzione n. 1: in assenza di strutture una classe Java leggera è il modo migliore per restituire più valori come una singola unità. p>     

risposta data 13.04.2016 - 17:28
fonte
5

Come altri già hanno sottolineato, il # 1 è la soluzione migliore.

Sul lato dell'implementazione, potresti dare un'occhiata al Pattern di notifica

Una notifica è "Un oggetto che raccoglie informazioni su errori e altre informazioni nel livello dominio e lo comunica alla presentazione."

Quindi, l'idea essenziale è quella di raggruppare gli errori (o gli avvertimenti, se lo si desidera) in un singolo oggetto.

    
risposta data 13.04.2016 - 11:04
fonte
2

Se il chiamante deve essere in grado di reagire agli avvertimenti, il modo preferito è la prima proposta, cioè restituire un tipo personalizzato (come altri hanno già indicato).

Tuttavia, la maggior parte delle volte, il chiamante non è interessato agli avvisi. In questi casi, tali avvisi di solito vengono inviati a un Logger (vedi ad esempio slf4j ).

public double[] performCalculation(double[] someInData) {
    // ...
    if (dataOutOfRange) {
        LOGGER.warn("Input data {} is out of range!", data);
    }
    // ...
    return result;
}

Un altro modo sarebbe lasciare che il chiamante passi in qualche tipo di ascoltatore, usando espressioni lambda Java 8.

public double[] performCalculation(double[] someInData, Consumer<Warning> warningConsumer) {
    // ...
    if (dataOutOfRange) {
        warningConsumer.accept(new Warning(WarningType.OUT_OF_RANGE, value));
    }
    // ...
    return result;
}
    
risposta data 13.04.2016 - 16:12
fonte
1

Penso che l'opzione 1 sia la migliore, ma a mio parere c'è un modo migliore per farlo rispetto alla creazione di una classe Risultati personalizzata.

Utilizza semplicemente un 2Tuple, come altri hanno menzionato, sul risultato e sull'elenco di avvisi.

public Tuple2<double[],String[]> performCalculation(double[] someInData){
    ...
}

Ecco un esempio di come implementare una tupla

Credo che sia meglio perché in generale le classi dovrebbero essere riutilizzabili. Una classe di risultati che usi una volta per ottenere questi risultati non lo è. Ma una Tupla è generale e può essere riutilizzata in molti casi.

    
risposta data 13.04.2016 - 17:40
fonte
0

Ho usato un approccio diverso in passato. Nel mio scenario ho avuto un gran numero di metodi che potrebbero generare avvisi. Ho creato una classe Context, che contiene varie cose, inclusa una lista di avvertimenti. Ha anche un metodo statico getCurrentContext ().

Quindi, ogni volta che devo generare un avviso, posso fare:

Context.getCurrentContext().addWarning("Email address is longer than 64 characters; truncating");

Il vantaggio di questa disposizione è che un avviso può essere creato ovunque.

La difficoltà principale è l'implementazione di getCurrentContext. La mia applicazione era un'applicazione web (un servizio REST / JSON), in cui ogni richiesta è gestita in un thread separato. All'inizio di una richiesta, un contesto vuoto viene creato e memorizzato in base all'ID thread. getCurrentContext utilizza l'ID corrente del thread per ottenere il contesto. Alla fine della richiesta, quando viene creato l'output JSON, eventuali avvisi sono inclusi nel JSON.

Nel tuo scenario, se performCalculation è l'unico metodo che può causare avvertimenti, allora probabilmente questa non è la soluzione per te.

Sarei interessato al feedback su questo approccio generale. Funziona molto bene per questa particolare app, ma sembra un po 'hacky.

    
risposta data 14.04.2016 - 12:09
fonte

Leggi altre domande sui tag