Domanda

È qualcosa che mi ha infastidito in ogni lingua che ho usato, ho un'istruzione if ma la parte condizionale ha così tanti controlli che devo dividerla su più righe, usare un'istruzione if nidificata o semplicemente accettare che sia brutta e andare avanti con la mia vita.

Ci sono altri metodi che hai trovato che potrebbero essere utili a me e a chiunque altro abbia riscontrato lo stesso problema?

Esempio, tutto su una riga:

if (var1 = true && var2 = true && var2 = true && var3 = true && var4 = true && var5 = true && var6 = true)
{

Esempio, multilinea:

if (var1 = true && var2 = true && var2 = true
 && var3 = true && var4 = true && var5 = true
 && var6 = true)
{

Annidato nell'esempio:

if (var1 = true && var2 = true && var2 = true && var3 = true)
{
     if (var4 = true && var5 = true && var6 = true)
     {
È stato utile?

Soluzione

Separare la condizione in diversi booleani e quindi utilizzare un booleano principale come condizione.

bool isOpaque = object.Alpha == 1.0f;
bool isDrawable = object.CanDraw && object.Layer == currentLayer;
bool isHidden = hideList.Find(object);

bool isVisible = isOpaque && isDrawable && ! isHidden;

if(isVisible)
{
    // ...
}

Meglio ancora:

public bool IsVisible {
    get
    {
        bool isOpaque = object.Alpha == 1.0f;
        bool isDrawable = object.CanDraw && object.Layer == currentLayer;
        bool isHidden = hideList.Find(object);

        return isOpaque && isDrawable && ! isHidden;
    }
}

void Draw()
{
     if(IsVisible)
     {
         // ...
     }
}

Assicurati di dare un nome alle tue variabili che indichi effettivamente l'intenzione piuttosto che la funzione.Ciò aiuterà molto lo sviluppatore a mantenere il tuo codice...potresti essere TU!

Altri suggerimenti

Sono sorpreso che nessuno l'abbia ancora ricevuto.Esiste un refactoring specifico per questo tipo di problema:

http://www.refactoring.com/catalog/decomposeConditional.html

Ci sono due questioni da affrontare qui:leggibilità e comprensibilità

La soluzione della "leggibilità" è una questione di stile e come tale è aperta all'interpretazione.La mia preferenza è questa:

if (var1 == true && // Explanation of the check
    var2 == true && // Explanation of the check
    var3 == true && // Explanation of the check
    var4 == true && // Explanation of the check
    var5 == true && // Explanation of the check
    var6 == true)   // Explanation of the check
    { }

o questo:

if (var1 && // Explanation of the check
    var2 && // Explanation of the check
    var3 && // Explanation of the check
    var4 && // Explanation of the check
    var5 && // Explanation of the check
    var6)   // Explanation of the check
    { }

Detto questo, questo tipo di controllo complesso può essere piuttosto difficile da analizzare mentalmente durante la scansione del codice (specialmente se non sei l'autore originale).Prendi in considerazione la creazione di un metodo di supporto per eliminare parte della complessità:

/// <Summary>
/// Tests whether all the conditions are appropriately met
/// </Summary>
private bool AreAllConditionsMet (
    bool var1,
    bool var2,
    bool var3,
    bool var4,
    bool var5,
    bool var6)
{
    return (
        var1 && // Explanation of the check
        var2 && // Explanation of the check
        var3 && // Explanation of the check
        var4 && // Explanation of the check
        var5 && // Explanation of the check
        var6);  // Explanation of the check
}

private void SomeMethod()
{
    // Do some stuff (including declare the required variables)
    if (AreAllConditionsMet (var1, var2, var3, var4, var5, var6))
    {
        // Do something
    }
}

Ora, quando si esegue la scansione visiva del metodo "SomeMethod", l'effettiva complessità della logica del test viene nascosta ma il significato semantico viene preservato affinché gli esseri umani possano comprenderlo ad alto livello.Se lo sviluppatore ha davvero bisogno di comprendere i dettagli, è possibile esaminare il metodo AreAllConditionsMet.

Penso che questo sia formalmente noto come modello di refactoring "Decompose Conditional".Strumenti come Resharper o Refactor Pro!può rendere facile questo tipo di refactoring!

In tutti i casi, la chiave per avere un codice leggibile e comprensibile è utilizzare nomi di variabili realistici.Anche se capisco che questo è un esempio inventato, "var1", "var2", ecc. lo sono non nomi di variabili accettabili.Dovrebbero avere un nome che rifletta la natura sottostante dei dati che rappresentano.

Li dividerò spesso in variabili booleane componenti:

bool orderValid = orderDate < DateTime.Now && orderStatus != Status.Canceled;
bool custValid = customerBalance == 0 && customerName != "Mike";
if (orderValid && custValid)
{
...

Innanzitutto, rimuoverei tutto il file == true parti, questo lo renderebbe più corto del 50% ;)

Quando ho una grande condizione cerco le ragioni.A volte vedo che dovrei usare il polimorfismo, a volte devo aggiungere qualche oggetto di stato.Fondamentalmente, implica che sia necessario un refactoring (un odore di codice).

A volte uso Le leggi di De Morgan per semplificare un po' le espressioni booleane.

Guardare Modelli di implementazione di Kent Beck.C'è uno schema particolare a cui sto pensando che potrebbe aiutare in questa situazione...si chiama "Guardie".Invece di avere tonnellate di condizioni, puoi suddividerle in una guardia, che chiarisce quali sono le condizioni avverse in un metodo.

Quindi, ad esempio, se hai un metodo che fa qualcosa, ma ci sono determinate condizioni in cui non dovrebbe fare qualcosa, piuttosto che:

public void doSomething() {
    if (condition1 && condition2 && condition3 && condition4) {
        // do something
    }
}

Potresti cambiarlo in:

public void doSomething() {
    if (!condition1) {
        return;
    }

    if (!condition2) {
        return;
    }

    if (!condition3) {
        return;
    }

    if (!condition4) {
        return;
    }

    // do something
}

È un po' più dettagliato, ma molto più leggibile, soprattutto quando inizi ad avere strani annidamenti, la guardia può aiutarti (in combinazione con i metodi di estrazione).

A proposito, consiglio vivamente quel libro.

Ho visto molte persone ed editori rientrare ciascuna condizione nella tua istruzione if con una scheda o abbinarla alla parentesi aperta:

if (var1 == true
    && var2 == true
    && var3 == true
   ) {
    /* do something.. */
}

Di solito inserisco la parentesi chiusa sulla stessa riga dell'ultima condizione:

if (var1 == true
    && var2 == true
    && var3 == true) {
    /* do something.. */
}

Ma non penso che questo sia altrettanto pulito.

Il consiglio di Steve Mcconell, da Codice completato:Utilizzare una tabella multidimensionale.Ogni variabile funge da indice per la tabella e l'istruzione IF si trasforma in una ricerca della tabella.Ad esempio if (size == 3 && peso> 70) si traduce nella decisione di ingresso della tabella [dimensione] [pesi_group

Prova a guardare Funtori e Predicati.Il progetto Apache Commons dispone di un ottimo set di oggetti che consentono di incapsulare la logica condizionale negli oggetti.Un esempio del loro utilizzo è disponibile su O'reilly Qui.Estratto dell'esempio di codice:

import org.apache.commons.collections.ClosureUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.functors.NOPClosure;

Map predicateMap = new HashMap();

predicateMap.put( isHonorRoll, addToHonorRoll );
predicateMap.put( isProblem, flagForAttention );
predicateMap.put( null, ClosureUtils.nopClosure() );

Closure processStudents = 
    ClosureUtils.switchClosure( predicateMap );

CollectionUtils.forAllDo( allStudents, processStudents );

Ora i dettagli di tutti questi predicati isHonorRoll e le chiusure utilizzate per valutarli:

import org.apache.commons.collections.Closure;
import org.apache.commons.collections.Predicate;

// Anonymous Predicate that decides if a student 
// has made the honor roll.
Predicate isHonorRoll = new Predicate() {
  public boolean evaluate(Object object) {
    Student s = (Student) object;

    return( ( s.getGrade().equals( "A" ) ) ||
            ( s.getGrade().equals( "B" ) && 
              s.getAttendance() == PERFECT ) );
  }
};

// Anonymous Predicate that decides if a student
// has a problem.
Predicate isProblem = new Predicate() {
  public boolean evaluate(Object object) {
    Student s = (Student) object;

    return ( ( s.getGrade().equals( "D" ) || 
               s.getGrade().equals( "F" ) ) ||
             s.getStatus() == SUSPENDED );
  }
};

// Anonymous Closure that adds a student to the 
// honor roll
Closure addToHonorRoll = new Closure() {
  public void execute(Object object) {
    Student s = (Student) object;

    // Add an award to student record
    s.addAward( "honor roll", 2005 );
    Database.saveStudent( s );
  }
};

// Anonymous Closure flags a student for attention
Closure flagForAttention = new Closure() {
  public void execute(Object object) {
    Student s = (Student) object;

    // Flag student for special attention
    s.addNote( "talk to student", 2005 );
    s.addNote( "meeting with parents", 2005 );
    Database.saveStudent( s );
  }
};

Bene, prima di tutto, perché no:

if (var1 && var2 && var2 && var3 && var4 && var5 && var6) {
...

Inoltre, è molto difficile eseguire il refactoring di esempi di codice astratto.Se mostrassi un esempio specifico, sarebbe più facile identificare un modello migliore per adattarlo al problema.

Non è meglio, ma quello che ho fatto in passato:(Il seguente metodo impedisce il cortocircuito dei test booleani, tutti i test vengono eseguiti anche se il primo è falso.Non è uno schema consigliato a meno che tu non sappia che devi sempre eseguire tutto il codice prima di tornare -- Grazie a ptomato per aver individuato il mio errore!)

booleano ok = cond1;
ok &= cond2;
ok &= cond3;
ok &= cond4;
ok &= cond5;
ok &= cond6;

Che è lo stesso di: (non è la stessa cosa, vedi nota sopra!)

ok = (cond1 && cond2 && cond3 && cond4 && cond5 && cond6);

Ricorro a valori booleani separati:

Bool cond1 == (var1 && var2);
Bool cond2 == (var3 && var4);

if ( cond1 && cond2 ) {}

Come altri hanno già detto, analizzerei i tuoi condizionali per vedere se c'è un modo per esternalizzarli ad altri metodi per aumentare la leggibilità.

Nei linguaggi riflessivi come PHP, puoi utilizzare variabili-variabili:

$vars = array('var1', 'var2', ... etc.);
foreach ($vars as $v)
    if ($$v == true) {
        // do something
        break;
    }

Mi piace suddividerli per livello, quindi formatterei il tuo esempio in questo modo:

if (var1 = true
 && var2 = true
 && var2 = true
 && var3 = true
 && var4 = true
 && var5 = true
 && var6 = true){

È utile quando hai più annidamenti, come questo (ovviamente le condizioni reali sarebbero più interessanti di "= true" per tutto):

if ((var1 = true && var2 = true)
 && ((var2 = true && var3 = true)
  && (var4 = true && var5 = true))
 && (var6 = true)){

Se ti capita di programmare in Python, è un gioco da ragazzi con il built-in all() funzione applicata all'elenco delle variabili (qui userò solo valori letterali booleani):

>>> L = [True, True, True, False, True]
>>> all(L) # True, only if all elements of L are True.
False
>>> any(L) # True, if any elements of L are True.
True

Esiste una funzione corrispondente nella tua lingua (C#?Giava?).Se è così, questo è probabilmente l'approccio più pulito.

McDowell,

Hai ragione quando usi il singolo operatore "&" che entrambi i lati dell'espressione valutano.Tuttavia, quando si utilizza l'operatore '&&' (almeno in C#), la prima espressione che restituisce false è l'ultima espressione valutata.Ciò rende l'inserimento della valutazione prima dell'istruzione FOR altrettanto valido di qualsiasi altro modo di farlo.

@tweakt

Non è meglio, ma quello che ho fatto in passato:

booleano ok = cond1;ok &= cond2;ok &= cond3;ok &= cond4;ok &= cond5;ok &= cond6;

Che è lo stesso di:

ok = (cond1 && cond2 && cond3 && cond4 && cond5 && cond6);

In realtà, queste due cose non sono le stesse nella maggior parte delle lingue.La seconda espressione in genere smetterà di essere valutata non appena una delle condizioni è falsa, il che può rappresentare un notevole miglioramento delle prestazioni se la valutazione delle condizioni è costosa.

Per motivi di leggibilità, personalmente preferisco la proposta di Mike Stone sopra.È facile commentare dettagliatamente e preserva tutti i vantaggi computazionali derivanti dalla possibilità di uscire anticipatamente.Puoi anche eseguire la stessa tecnica in linea in una funzione se confonderebbe l'organizzazione del tuo codice per spostare la valutazione condizionale lontano dall'altra funzione.È un po' banale, ma puoi sempre fare qualcosa del tipo:

do {
    if (!cond1)
       break;
    if (!cond2)
       break;
    if (!cond3)
       break;
    ...
    DoSomething();
} while (false);

il while (falso) è un po' scadente.Vorrei che le lingue avessero un operatore di ambito chiamato "once" o qualcosa da cui potresti uscire facilmente.

Se lo facessi in Perl, ecco come potrei eseguire i controlli.

{
  last unless $var1;
  last unless $var2;
  last unless $var3;
  last unless $var4;
  last unless $var5;
  last unless $var6;

  ... # Place Code Here
}

Se prevedi di utilizzarlo su una subroutine, sostituisci ogni istanza di last con return;

Mi piace suddividere ciascuna condizione in variabili descrittive.

bool isVar1Valid, isVar2Valid, isVar3Valid, isVar4Valid;
isVar1Valid = ( var1 == 1 )
isVar2Valid = ( var2.Count >= 2 )
isVar3Valid = ( var3 != null )
isVar4Valid = ( var4 != null && var4.IsEmpty() == false )
if ( isVar1Valid && isVar2Valid && isVar3Valid && isVar4Valid ) {
     //do code
}
    if (   (condition_A)
        && (condition_B)
        && (condition_C)
        && (condition_D)
        && (condition_E)
        && (condition_F)
       )
    {
       ...
    }

al contrario di

    if (condition_A) {
       if (condition_B) {
          if (condition_C) {
             if (condition_D) {
                if (condition_E) {
                   if (condition_F) {
                      ...
                   }
                }
             }
          }
       }
    }

E

    if (   (   (condition_A)
            && (condition_B)
           )
        || (   (condition_C)
            && (condition_D)
           )
        || (   (condition_E)
            && (condition_F)
           )
       )
    {
       do_this_same_thing();
    }

al contrario di

    if (condition_A && condition_B) {
       do_this_same_thing();
    }
    if (condition_C && (condition_D) {
       do_this_same_thing();
    }
    if (condition_E && condition_F) {
       do_this_same_thing();
    }

La maggior parte degli strumenti di analisi statica per l'esame del codice si lamenteranno se più espressioni condizionali non utilizzano parentesi esplicite che dettano l'analisi delle espressioni, invece di fare affidamento sulle regole di precedenza degli operatori e su un minor numero di parentesi.

L'allineamento verticale allo stesso livello di rientro delle parentesi graffe aperte/chiuse {}, delle parentesi aperte e chiuse (), delle espressioni condizionali con parentesi e operatori a sinistra è una pratica molto utile, che MIGLIORA notevolmente la leggibilità e la chiarezza del codice invece di inceppare tutto che può eventualmente essere incastrato su una singola riga, senza allineamento verticale, spazi o parentesi

Le regole di precedenza degli operatori sono complicate, ad es.&& ha una precedenza maggiore di ||, ma | ha la precedenza di &&

COSÌ, ...

    if (expr_A & expr_B || expr_C | expr_D & expr_E || expr_E && expr_F & expr_G || expr_H {
    }

è un'espressione condizionale multipla davvero semplice da leggere e valutare in modo improprio per i semplici esseri umani.

    if (   (  (expr_A)
            & (expr_B)
           )
        || (  (expr_C)
            | (  (expr_D)
               & (expr_E)
              )
           )
        || (   (expr_E)
            && (  (expr_F)
                & (expr_G)
               )
           )
        || (expr_H)
       )
    {
    }

Non c'è niente di sbagliato nello spazio orizzontale (riga), nell'allineamento verticale o nelle parentesi esplicite che guidano la valutazione dell'espressione, tutte cose che MIGLIORANO la leggibilità e la chiarezza

Se lo fai:

if (var1 == true) {
    if (var2 == true) {
        if (var3 == true) {
            ...
        }
    }
}

Quindi puoi anche rispondere ai casi in cui qualcosa non è vero.Ad esempio, se stai convalidando l'input, potresti dare all'utente un suggerimento su come formattarlo correttamente o altro.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top