Le dichiarazioni try-catch di nidificazione sono ancora un odore di codice se nidificate all'interno di un ciclo?

5

Ho sentito che le dichiarazioni try-catch di annidamento possono spesso essere un odore di codice, quindi mi chiedo se questa situazione sia un'eccezione. In caso contrario, quali sarebbero i metodi migliori per il refactoring?

Il mio codice ha il seguente aspetto:

try{
    X x = blah;
    otherStuff;
    for (int i = 0; i < N; i++){
        try{
            String y = Integer.toString(i);
            f(x);
        }
        catch(Exceptions1 e1){
            System.err.println(... + y + ...);
            System.exit(0);
        }
}
catch(Exceptions2 e2){
    ...
}

Sto usando Exceptions qui per indicare un multi-catch.

e2 è usato per catturare le eccezioni generate inizializzando x e facendo otherStuff . Idealmente, avrei avuto il mio try-catch surround solo su quelle due linee, ma io uso x nel mio ciclo e volevo evitare gli avvertimenti "assegnazione inutilizzata" causati dall'inizializzazione su null all'esterno del try-catch.

e1 non è stato multi-catch con e2 perché voglio fornire all'utente informazioni sui dettagli di iterazione e quindi desideravo un blocco catch all'interno del ciclo.

    
posta Sean Scott 02.06.2016 - 18:57
fonte

3 risposte

4

Se il catch di e2 è, come dici tu, solo per rilevare errori nell'inizializzazione di x e facendo otherStuff potresti estrarlo in un metodo separato. Questo separa anche la logica in modo appropriato e ti consente potenzialmente di dare un nome significativo a otherStuff .

public X Foo() {
    try{
        X x = blah;
        otherStuff;

        return x;
    }
    catch(Exceptions2 e2){
        ...
    }
}

public void Bar() {    
    X x = Foo();
    for (int i = 0; i < N; i++){
        try{
            String y = Integer.toString(i);
            f(x);
        }
        catch(Exceptions1 e1){
            System.err.println(... + y + ...);
            System.exit(0);
        }
    }
}
    
risposta data 03.06.2016 - 15:30
fonte
7

A meno che non intendi elaborare l'intero loop interno indipendentemente dal verificarsi di un'eccezione, il tuo codice è sostanzialmente equivalente a

try{
    X x = blah;
    otherStuff;
    for (...){ 
       f(x)
    }
}
catch(Exceptions1 e1){
    ...
}
catch(Exceptions2 e2){
    ...
}

che non richiede la nidificazione.

Se hai ancora bisogno della gestione delle eccezioni interne, rifatta l'eccezione interna e il suo metodo di inclusione in un nuovo metodo. Questo ha anche il vantaggio di costringervi a pensare ad altri modi per elaborare il ciclo interno; le eccezioni possono diventare molto costose in termini di prestazioni, se vengono lanciate molte di esse.

    
risposta data 02.06.2016 - 19:08
fonte
3

Ho alcune osservazioni sulla tua domanda:

  1. Nell'esempio che hai fornito non hai bisogno di eccezioni. Dovresti solo controllare la stringa y, registrare qualsiasi errore ed uscire dal ciclo. Niente è eccezionale se non si è validi e si ha bisogno di essere registrati. Il fatto che stai usando le eccezioni in questo caso è l'indicazione dell'odore del codice.

  2. Le eccezioni Multi-Catch non sono pensate per essere utilizzate per avvolgere grandi quantità di codice e catturare tutto ciò che potrebbe andare storto. Sono pensati per aiutare a distinguere tra le eccezioni nel caso in cui una singola sezione di codice possa produrre una serie di eccezioni diverse. Dal momento che stai usando le eccezioni di cattura multipla come una sorta di approccio "catch all", ciò indica odore di codice.

  3. Non ha senso inserire un catch di prova all'interno del ciclo a meno che non si preveda di continuare a scorrere il resto del ciclo anche se si trova un'eccezione su uno degli elementi. Il tuo codice di esempio mostra che prevedi di uscire dal ciclo se un elemento è eccezionale.

Penso che potresti stare meglio con qualcosa del tipo:

try
{
     var x = blah;
     DoStuff();
     foreach(var i in icollection) // i is an int
     {
          var y = SomethingDependentOnI(i);

          // check y for explicit exceptional states
          if (IsSomethingWrongWithY(y))
               throw new Exception1(y);
     }
}
catch (Exception1 e1)
{
}
catch (Exception2 e2)
{
}

Questo è molto più chiaro, ma soffre ancora dei problemi descritti sopra.

    
risposta data 03.06.2016 - 13:35
fonte

Leggi altre domande sui tag