Qual è un modello migliore (stile di codifica) per la convalida degli argomenti - ostacolo (barriera) o recinzione? [duplicare]

9

Non so se ci sono nomi accettati per questi pattern (o anti-pattern), ma mi piace chiamarli come li chiamo qui. In realtà, sarebbe Domanda 1: quali sono i nomi accettati per questi pattern, se ce ne sono?

Supponiamo che esista un metodo che accetta una serie di parametri e che devi verificare l'input non valido prima di eseguire il codice effettivo del metodo:

public static void myMethod (String param1, String param2, String param3)

Stile di ostacolo

Lo chiamo così perché è come un ostacolo che un corridore di pista deve saltare per arrivare al traguardo. Puoi anche considerarli come barriere condizionali.

{
    if (param1 == null || param1.equals("")) {
        // some logging if necessary
        return; // or throw some Exception or change to a default value
    }

    if (param2 == null || param2.equals("")) {
        // I'll leave the comments out
        return;
    }

    if (param3 == null || param3.equals("")) {
        return;
    }

    // actual method code goes here.
}

Quando i controlli sono per una certa piccola sezione in un metodo più grande (e la sezione non può essere spostata su un metodo privato più piccolo), è possibile utilizzare i blocchi etichettati con le istruzioni break :

{
    // method code before block

    myLabel:
    {
        if (param1 ... // I'll leave out the rest for brevity
            break myLabel;
        if (param2 ...
            break myLabel;
        ...

        // code working on valid input goes here

    } // 'break myLabel' will exit here

    // method code after block
}

Stile recinzione

Ciò circonda il codice con una fence che ha un gate condizionale che deve essere aperto prima di poter accedere al codice. Recinzioni annidate significherebbe più porte per raggiungere il codice (come una bambola russa).

{
    if (param1 != null && !param1.equals("")) {
        if (param2 != null && !param2.equals("")) {
            if (param3 != null && !param3.equals("")) {

                // actual method code goes here.

            } else {
                // some logging here
            }
        } else {
            // some logging here
        }
    } else {
        // some logging here
    }
}

Potrebbe anche essere riscritto come segue. Le istruzioni di registrazione sono accanto ai controlli, anziché essere dopo il codice del metodo effettivo.

{
    if (param1 == null || param1.equals("")) {
        // some logging here

    } else if (param2 == null || param2.equals("")) {
        // some logging here

    } else if (param3 == null || param3.equals("")) {
        // some logging here

    } else {

        // actual method code goes here.

    }
}

Domanda 2: quale stile è migliore e perché?

Domanda 3: ci sono altri stili?

I personally prefer hurdle style because it looks easier on the eyes and does not keep indenting the code to the right every time there's a new parameter. It allows intermittent code between checks, and it's neat, but it's also a little difficult to maintain (several exit points).

The first version of fence style quickly gets really ugly when adding parameters, but I suppose it's also easier to understand. While the second version is better, it can be broken accidentally by a future coder, and does not allow intermittent code between conditional checks.

    
posta ADTC 09.09.2014 - 16:48
fonte

2 risposte

19

Il tuo primo stile "ostacolo" è superiore sotto ogni aspetto.

  • Ti fa risparmiare molti livelli di indentazione. Questo da solo rende molto più semplice da capire.
  • È diff friendly: l'aggiunta di un altro vincolo non richiede di ri-indentare tutto il codice.
  • La validazione si trova in one place - nella parte superiore della funzione. Con l'altro stile, è prima e dopo il codice principale. Mettere tutto il codice di validazione in un posto riduce il carico cognitivo.
  • I punti di uscita multipli non contano molto in una lingua con la garbage collection. La ricerca di un singolo return è una reliquia dei tempi antichi in cui tutto doveva essere deallocato manualmente.
risposta data 09.09.2014 - 17:13
fonte
7

Il tuo esempio riguarda specificamente il controllo che il codice chiamante è conforme al contratto . Se questo è il caso, lo stile ad ostacoli è in genere più pulito e più facile da leggere, solitamente perché si genera un'eccezione, quindi non ci si può aspettare che ci sia un altro pezzo di codice più in basso nel metodo che mancherà di essere eseguito.

C'è un altro caso in cui stai decomponendo la funzione in due parti (simile alla corrispondenza del modello nei linguaggi funzionali). Considera il caso di una funzione ricorsiva:

int sum(int upTo)
{
    if(upTo<= 0) return 0;

    int previousSum = sum(upTo-1);
    return previousSum + upTo;
}  

Se stai solo verificando l'interruzione dei criteri, lo stile di ostacolo è ancora tipico.

C'è un altro caso in cui devi fare qualcos'altro dopo la tua decisione (questo è meno comune):

int sum(int upTo)
{
    int result;
    if(counter <= 0) 
    {
        result = 0;
    }
    else
    {
        int previousSum = sum(upTo-1);
        result = previousSum + upTo;
    }

    Console.WriteLine("Intermediate sum result: {0}", result);

    return result;
}  

In questo caso è tipico usare lo stile di recinzione. Tieni presente che l'utilizzo dello stile di ostacolo qui genera un risultato diverso, perché devi scrivere la riga Console due volte o non sputare la riga quando il valore è zero.

Lo stile scelto comunica al lettore che tipo di funzione è. Se trovi che la funzione o il metodo diventa così lungo da non essere chiaro, allora considera di suddividerlo in più funzioni per renderlo più chiaro.

In genere questo dovrebbe essere cambiato in uno stile di ostacolo con una funzione di supporto:

int sum(int upTo)
{
    int result = sumHelper(upTo);
    Console.WriteLine("Intermediate sum result: {0}", result);
    return result;
}  

int sumHelper(int upTo)
{
    if(upTo<= 0) return 0;

    int previousSum = sum(upTo-1);
    return previousSum + upTo;
}

TL; DR - L'ostacolo è il più comune, ma scegli lo stile appropriato per la tua situazione specifica e assicurati che le tue funzioni siano abbastanza corte / semplici da essere ovviamente corrette in ogni caso.

    
risposta data 09.09.2014 - 17:29
fonte