Usa un altro dopo l'eccezione (o no)


9

Considera questo bit di codice:

if (x == 1)
{
  throw "no good; aborting" ;
}

[... more code ...]

Ora considera questo codice:

if (x == 1)
{
  throw "no good; aborting" ;
}
else
{
  [... more code ...]
}

I due casi funzionano esattamente allo stesso modo. Il primo caso ha il vantaggio di non dover "racchiudere" il resto del codice in un else. Il secondo ha il vantaggio di seguire la pratica di avere esplicitamente un elseper ogni if.

Qualcuno può fornire solidi argomenti a favore dell'uno rispetto all'altro?


5
La pratica che citi a favore dell'esplicito elsesembra falsa. Abbastanza spesso, semplicemente non c'è niente da mettere nel elseblocco a meno che non ti pieghi all'indietro.

Consistenza? Consentire la solidità di fronte alla modifica del codice? Leggibilità?
Thomas Eding,

1
Ehhh, non sono così un fan dell'idea che ogni ha ifbisogno di un else. L'ultimo programmatore che ha lavorato sulla nostra base di codice lo ha seguito rigidamente ( beh, a volte ... è un po 'schizofrenico ). Di conseguenza, abbiamo un sacco di else { /* do nothing */ }blocchi completamente privi di significato che sporcano il codice ...
KChaloux,

4
"Un altro per ogni se" sembra un bizzarro annuncio emesso da un architetto di nuvole in nome di (una sciocca) coerenza. Non vedo alcun vantaggio nel seguire quella pratica e non ne ho mai sentito parlare da nessuna parte.
Erik Dietrich,

È un altro ridondante. Se stai lavorando con lo stack .NET, installa ReSharper e ti ricorderà di rimuovere tutte le altre istruzioni ridondanti.
CodeART

Risposte:


16

Non è necessario aggiungere elsedopo i iframi che interrompono il flusso di controllo incondizionatamente , come quelli contenenti a throwo a return. Ciò migliorerebbe la leggibilità del programma rimuovendo il livello non necessario di annidamento introdotto dal elseramo.

Ciò che sembra più o meno OK con un singolo throwdiventa veramente brutto quando vengono invenduti più tiri di fila:

void myMethod(int arg1, int arg2, int arg3) {
    // This is demonstrably ugly - do not code like that!
    if (!isValid(arg1)) {
        throw new ArgumentException("arg1 is invalid");
    } else {
        if (!isValid(arg2)) {
            throw new ArgumentException("arg2 is invalid");
        } else {
            if (!isValid(arg3)) {
                throw new ArgumentException("arg3 is invalid");
            } else {
                // The useful code starts here
            }
        }
    }
}

Questo frammento fa la stessa cosa, ma sembra molto meglio:

void myMethod(int arg1, int arg2, int arg3) {
    if (!isValid(arg1)) {
        throw new ArgumentException("arg1 is invalid");
    }
    if (!isValid(arg2)) {
        throw new ArgumentException("arg2 is invalid");
    }
    if (!isValid(arg3)) {
        throw new ArgumentException("arg3 is invalid");
    }
    // The useful code starts here
}

+1 Vero. Il secondo caso OP ti costringe a leggere attentamente, quindi ti lascia con un WTF. Ma ... cerca sempre di abbreviare i metodi. Anche un ritorno nel mezzo di un metodo di 200 righe è negativo.
Tulains Córdova,

1
Ad essere onesti, se stai usando solo ripetuti if puoi farlo else if.
Guvante,

2
@Guvante: Ognuno ifverifica una singola condizione e la risolve se la condizione è vera e, a meno che non ci siano cose alternative che devono accadere se la condizione è falsa, else ifnon sono necessarie. Abbiamo un termine nel mio ufficio per codice come il primo frammento di dasblinkenlight: " macchine per pachinko ".
Blrfl,

@Blrfl pachinko machines hah, analogia perfetta +1
Jimmy Hoffa

@Blrfl: stavo facendo riferimento al fatto che gli if ripetuti sono un cattivo esempio di nidificazione eccessiva. Non dovresti annidare ripetuti if comunque. Sono d'accordo che in generale, a meno che tu non stia parlando di una piccola quantità di codice, non c'è motivo di includerlo else.
Guvante,

5

Definirei la pratica "esplicita dell'altro" a cui ti riferisci come anti-pattern, in quanto oscura il fatto che non esiste un codice di caso speciale come altro per il tuo if.

La leggibilità / manutenibilità è generalmente migliorata quando per lo più non hai altro che costrutti di flusso di codice necessari e li minimizzi. Ciò significa che gli altri ridondanti e gli if che aggiungono un ambito a un'intera funzione rendono più difficile seguirlo e mantenerlo.

Supponiamo che tu abbia questa funzione:

public void ConfigureOblogon(Oblogon oblogonToConfigure)
{
    if (_validColors.Contains(oblogonToConfigure.Color))
    {
        oblogonToConfigure.ColorIndex = _validColors.IndexOf(oblogonToConfigure.Color);
    }
    else
    {
        oblogonToConfigure.Color = _validColors[0];
        oblogonToConfigure.ColorIndex = 0;
    }
}

Ora arriva il requisito che durante la configurazione dovresti anche specificare l'indice tipo / tipo dell'oblogon, ci sono più ambiti in cui qualcuno potrebbe inserire quel codice e finire con un codice non valido, ad es.

public void ConfigureOblogon(Oblogon oblogonToConfigure)
{
    if (!_validOblogons.Contains(oblogonToConfigure.Type))
    {
        oblogonToConfigure.Type = _validOblogons[0];
        oblogonToConfigure.TypeIndex = 0;
        if (_validColors.Contains(oblogonToConfigure.Color))
        {
            oblogonToConfigure.ColorIndex = _validColors.IndexOf(oblogonToConfigure.Color);
        }
        else
        {
            oblogonToConfigure.Color = _validColors[0];
            oblogonToConfigure.ColorIndex = 0;
        }
    }
    else
    {
        oblogonToConfigure.TypeIndex = _validOblogons.IndexOf(oblogonToConfigure.Type);
    }
}

Confronta questo con se il codice originale fosse scritto con costrutti di flusso di controllo minimi necessari e minimizzati a quello.

public void ConfigureOblogon(Oblogon oblogonToConfigure)
{
    if (!_validColors.Contains(oblogonToConfigure.Color))
    {
        oblogonToConfigure.Color = _validColors[0];
    }

    oblogonToConfigure.ColorIndex = _validColors.IndexOf(oblogonToConfigure.Color);
}

Ora sarebbe molto più difficile mettere accidentalmente qualcosa nello scopo sbagliato o finire con gli scopi gonfiore che causano la duplicazione nella crescita e nel mantenimento a lungo termine di questa funzione. Inoltre è ovvio quali sono i possibili flussi attraverso questa funzione, quindi la leggibilità è migliorata.

Lo so, l'esempio è un po 'inventato, ma ho visto molte volte

SomeFunction()
{
    if (isvalid)
    {
        /* ENTIRE FUNCTION */
    }
    /* Nothing should go here but something does on accident, and an invalid scenario is created. */
}

Quindi, formalizzare quelle regole sui costrutti del flusso di controllo, penso che possa aiutare le persone a sviluppare l'intuizione necessaria per annusare qualcosa quando iniziano a scrivere codice in quel modo. Quindi inizieranno a scrivere ..

SomeFunction()
{
    if (!isvalid)
    {
        /* Nothing should go here, and it's so small no one will likely accidentally put something here */
        return;
    }

    /* ENTIRE FUNCTION */
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.