Domanda

Se ho una funzione con un sacco di condizionali, qual è il modo migliore per organizzarla?

Ciò di cui sono preoccupato è che qualcun altro entri nel codice e capisca cosa sta succedendo. Anche se l'esempio è semplice, immagina che il condizionale sia molto complesso.

Per un esempio:

public void function(string value, string value2)
{
    if (value == null)
        return;

    if (value2 == value)
        DoSomething();
}

o

public void function(string value, string value2)
{
    if (value != null)
    {
        if (value2 == value)
            DoSomething();
    }
}

o

public void function(string value, string value2)
{
    if (value != null && value2 == value)
        DoSomething();
}
È stato utile?

Soluzione

Organizza le condizioni e inseriscile in un metodo.

ad esempio sostituirlo:

 if( a& & n || c  && ( ! d || e ) && f > 1 && ! e < xyz ) { 
      // good! planets are aligned.
      buyLotteryTicket();
 } else if( ..... oh my ... ) { 
 }

In questo:

if( arePlanetsAligned() ) { 
    buyLotteryTicket(); 
} else if( otherMethodHere() ) { 
   somethingElse();
}  

In questo modo non importa quale stile usi (1, 2 o 3) perché l'istruzione if descriverà chiaramente qual è la condizione da testare. Non sono necessari costrutti aggiuntivi.

Il punto è rendere il codice più chiaro e auto-documentato. Se si utilizza un linguaggio di programmazione OO, è possibile utilizzare un oggetto per memorizzare lo stato (variabili) ed evitare di creare metodi che accettano 5-10 parametri.

Queste sono domande simili:

Il modo migliore per sbarazzarsi di if annidati

Esiste un'alternativa a questo codice hyperidented

Il secondo collegamento mostra un modo più completo e complesso di trasformare un incubo orribile di tutti i manutentori in un codice auto-documentante.

Mostra come trasformarlo:

public String myFunc(SomeClass input)
{
    Object output = null;

    if(input != null)
    {
        SomeClass2 obj2 = input.getSomeClass2();
        if(obj2 != null)
        {
            SomeClass3 obj3 = obj2.getSomeClass3();
            if(obj3 != null && !BAD_OBJECT.equals(obj3.getSomeProperty()))
            {
                SomeClass4 = obj3.getSomeClass4();
                if(obj4 != null)
                {
                    int myVal = obj4.getSomeValue();
                    if(BAD_VALUE != myVal)
                    {
                        String message = this.getMessage(myVal);
                        if(MIN_VALUE <= message.length() &&
                           message.length() <= MAX_VALUE)
                        {
                            //now actually do stuff!
                            message = result_of_stuff_actually_done;
                        }
                    }
                }
            }
        }
    }
    return output;
}

in questo:

if ( isValidInput() && 
    isRuleTwoReady() &&
    isRuleTreeDifferentOf( BAD_OBJECT ) &&
    isRuleFourDifferentOf( BAD_VALUE ) && 
    isMessageLengthInRenge( MIN_VALUE , MAX_VALUE ) ) { 
            message = resultOfStuffActuallyDone();
}

Altri suggerimenti

Preferisco la prima opzione - fail fast è più pulito, più chiaro e più facile da leggere e comprendere.

Capisco che questo non è un fallimento ma il concetto si applica ancora. Non mi piacciono affatto le istruzioni nidificate if .

È possibile consultare la programmazione difensiva per garantire che il contratto per la funzionalità dei metodi possa essere adempiuto.

public void function(string value, string value2)
{
    if (string.IsNullOrEmpty(value1)) throw new ArgumentNullException("value1", "value 1 was not set");
    if (string.IsNullOrEmpty(value2)) throw new ArgumentNullException("value2", "value 2 was not set");

    DoSomething();
}

Rifattorizza quella roba nella sua propria funzione. È molto meglio leggere un nome di funzione descrittiva che un gruppo di espressioni booleane.

// leave complex conditional code out, so that we can focus on the larger problem in the function
public void function(string value, string value2)
{
    if (MyDescriptiveTestName)
    {
        DoSomething();
    }
}

// encapsulate complex conditional code so that we can focus solely on it.
private bool MyDescriptiveTestName(string value, string value2)
{
    if (value != null && value2 == value)
    {
        return true;
    }
    return false;
}

Posso raccomandare il libro Clean Code di Robert C. Martin che fornisce una vasta serie di euristiche per la scrittura di codice leggibile e gestibile.

Ora un'altra opzione sarebbe quella di estrarre il condizionale in un'altra funzione privata e nominarlo in modo che descriva il tuo intento. Non funziona troppo bene con il codice fornito in quanto generico, ma sarebbe simile a:

public void function(string value, string value2)
{
    if (valuesAreValidAndEqual(value, value2))
    {
        DoSomething();
    }
}

private void valuesAreValidAndEqual(string value, string value2)
{
    return value != null && value2 == value;
}

Chiaramente questo è più utile se i nomi delle variabili e i nomi delle funzioni sono correlati al tuo dominio.

Se hai una funzione con molti condizionali, userei un'istruzione switch - non ifs. Potrei anche suddividere i dettagli in diverse funzioni (o persino classi) se ciò è possibile.

Articoli di overflow dello stack correlati :

Mi piace la terza opzione, ma dipende molto dalla lingua. Supponete che nella terza parte la prima parte dell'istruzione fallisca e non esegua la seconda. Questo dipende dalla lingua. So che la maggior parte dei linguaggi basati su C lo fanno, ma dato che non ne hai specificato uno che è un potenziale problema. Ce ne potrebbe essere uno là fuori di cui non sono consapevole che non abbia il concetto di corto circuito.

Il fatto che stai pensando in anticipo alla leggibilità del codice è metà della battaglia.

Per quanto riguarda quale dei tuoi esempi è più leggibile, è piuttosto soggettivo.

Le mie opinioni sugli esempi forniti:

  • Personalmente, penso che il primo esempio sia più semplice seguire.
  • Riduzione al minimo dei livelli di annidamento e / o numero di condizionali di solito migliora la leggibilità.
  • Alcuni discuterebbero contro più punti di uscita da un metodo (come esempio 1), ma penso solo diventa un problema quando il metodo diventa davvero lungo. Non è davvero così grande se sei giusto controllare gli input e fallire rapidamente.

La mia preferenza è la seconda opzione. Personalmente, quando leggo codice del genere, tengo presente le condizioni in cui inserirò ogni livello nidificato. Nel primo esempio probabilmente dimenticherei che la prima condizione (valore == null è falso) continua a essere valida. Neanche il terzo è male, ma preferisco il secondo.

Il fatto che tu abbia molte istruzioni in una funzione è un segno che la funzione dovrebbe essere divisa in più piccole.

Non esiste un modo MIGLIORE per inserire dichiarazioni if, penso che le risposte siano soggettive.

La chiarezza è spesso una qualità difficile da giudicare. Ciò che ti è chiaro quando scrivi un pezzo di codice può essere completamente ottuso per qualcun altro - o anche per te stesso dopo che è trascorso abbastanza tempo.

Ecco le mie regole pratiche personali quando si crea una funzione con molti controlli condizionali:

  1. Raggruppa le condizioni in unità logiche, quando possibile
  2. Utilizza valori intermedi chiaramente definiti per semplificare la leggibilità e il debug.
  3. Evita di ripetere più volte gli stessi costrutti logici (vedi sopra).
  4. Se possibile, refactificare condizioni complesse o costose in funzioni separate.
  5. Se possibile, utilizzare le condizioni di uscita anticipata nella parte superiore del metodo per uscire, ma ...
  6. Evita le dichiarazioni di ritorno peppering in tutto il corpo del metodo.
  7. Non fare affidamento sulla precedenza dell'operatore, utilizzare le parentesi per raggruppare le dichiarazioni.
  8. Evita di fare affidamento sul rientro, usa i blocchi ({...}) anche con una sola istruzione, questo aiuta con la manutenibilità.
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top