Questa è una situazione valida per la restituzione piuttosto che un'eccezione?

3

Questo non è qualcosa che farei normalmente, ma ho una situazione in cui un codice legacy esistente viene riutilizzato in una nuova applicazione. Il codice è condiviso e deve essere utilizzato sia dalla legacy che dalla nuova applicazione. L'applicazione legacy è un'applicazione di reporting e gli errori vengono segnalati come messaggi in modo diverso rispetto ai messaggi informativi nell'interfaccia utente. La nuova applicazione esegue effettivamente azioni basate su questi calcoli: quindi gli errori sono ora fondamentali per l'esecuzione del programma.

Abbiamo centinaia di metodi di business logic che ordinano una persona in una serie di bucket, in cui i bucket sono contenuti in un dizionario (la chiave è il numero bucket, il valore è true se si trovano in quel bucket in base al calcolo): / p>

public Dictionary<int, bool> Calculation(IPerson person, out string message)
{
    try
    {
        var buckets = new Dictionary<int, bool>();

        // Do logic to determine what buckets the person is in
        // message is used by the legacy application UI...can be set to anything
    }
    catch(Exception)
    {
        message = "Some non-consistent error message";
    }

    return buckets;
}

In ogni metodo c'è un blocco di eccezioni onnicomprensive che inghiotte l'eccezione (sì ... lo so).

Ai fini di questi calcoli un errore di eccezione non significa che l'intero calcolo debba essere scartato: se c'è un'eccezione e la persona non finisce in tutti i bucket, non dovremmo preoccuparci. Il messaggio viene utilizzato per comunicare all'interfaccia utente che per questa persona l'intero calcolo potrebbe non essere stato completato e che è considerato sufficiente. Il messaggio può contenere anche informazioni non di errore ... non è specificamente un messaggio di errore e quando c'è un errore non c'è testo coerente da cercare.

Con la nuova applicazione, potenzialmente vorremmo intraprendere un percorso di esecuzione diverso se ci fosse un'eccezione: per lo meno vogliamo rilevare e visualizzare costantemente gli errori nell'interfaccia utente in modo ovvio e distinto, non solo come qualunque testo è stato assegnato alla variabile del messaggio. Vorremmo anche registrare i dettagli delle eccezioni in questa nuova applicazione.

Non sono autorizzato ad apportare modifiche che alterano il comportamento dell'applicazione legacy, ma posso ridefinire le firme del metodo. Sto proponendo che l'eccezione sia catturata com'è ora, ma restituirla è un parametro out:

public Dictionary<int, bool> Calculation(IPerson person, out string message, out Exception exception)
{
    try
    {
        var buckets = new Dictionary<int, bool>();

        // Do logic to determine what buckets the person is in
        // message is used by the legacy application UI...can be set to anything
    }
    catch(Exception ex)
    {
        message = "Some non-consistent error message";
        exception = ex;
    }

    return buckets;
}

So che questo è totalmente non standard e assolutamente non il modo in cui le eccezioni sono destinate ad essere utilizzate, ma in una situazione in cui vogliamo segnalare che si è verificata un'eccezione gestita ... perché non restituire l'eccezione stessa? tieni presente che non posso modificare il comportamento dell'applicazione precedente.

Pensieri? Questa è ancora una pessima idea? C'è un modo migliore per farlo dato i vincoli?

    
posta MrLane 30.07.2015 - 03:04
fonte

3 risposte

2

Penso che la necessità di non modificare il comportamento dell'applicazione precedente sia piuttosto restrittiva. Inoltre, sono completamente d'accordo con te sul fatto che la cosa giusta da fare è lanciare l'eccezione e catturarla dove deve essere catturata dall'interfaccia utente.

Detto questo, data la necessità di non modificare il comportamento, ritengo che la tua soluzione sia un compromesso accettabile, data la situazione. Fa quello che ti serve per le ragioni che hai dichiarato. L'unica modifica che apporterei è di generare sia message che Exception nella propria classe, ad es.

public Dictionary<int, bool> Calculation(IPerson person, out ErrorResult result) {
    try {
    // snip
    } catch (Exception ex) {
        result.message = "Some non-consistent error message";
        result.exception = ex;
    }
}

Principalmente a causa del principio di "oggetti correlati dovrebbero essere legati insieme nella stessa classe".

Potresti anche prendere in considerazione di includere sia la Dictionary che la ErrorResult class in una classe più grande per rimuovere il parametro out. In effetti, probabilmente farei qualcosa di simile:

public class Result {
    Dictionary<int, bool> result;
    List<ErrorResult> errors;
}

In questo modo puoi tenere traccia di tutte le possibili eccezioni generate e i loro messaggi. La tua firma del metodo diventerebbe quindi:

public Result Calculation(IPerson person) {
    
risposta data 30.07.2015 - 16:30
fonte
1

Che ne dici di una diversa modifica ai metodi:

Elimina tutti i metodi esistenti, sposta il codice in nuovi metodi. I metodi esistenti contengono solo l'eccezione di swallower e in caso contrario chiamano i nuovi metodi per eseguire il lavoro. Questo lascia il modulo precedente che puoi chiamare se lo vuoi inghiottire o il nuovo modulo se vuoi che lo lancino quando dovrebbe gettare.

Questo richiede meno modifiche effettive al codice (anche se richiede più spostamento di codice) e per quanto mi riguarda è molto più pulito del tuo approccio.

    
risposta data 30.07.2015 - 06:25
fonte
0

Penso che la cosa migliore da fare sia semplicemente utilizzare il blocco catch per registrare l'eccezione e qualsiasi altra informazione che ti aiuti a tracciare l'errore, inclusa una traccia dello stack. Questo soddisfa i chiamanti, che non devono cambiare le firme dei metodi, e soddisfa ulteriormente gli sviluppatori, che ora hanno un modo per diagnosticare i problemi relativi alle eccezioni generate.

    
risposta data 30.07.2015 - 04:40
fonte