Mantenibilità della logica booleana - Annidamento se sono necessarie istruzioni?


11

Quale di questi è meglio per la manutenibilità?

if (byteArrayVariable != null)
    if (byteArrayVariable .Length != 0)
         //Do something with byteArrayVariable 

O

if ((byteArrayVariable != null) && (byteArrayVariable.Length != 0))
  //Do something with byteArrayVariable 

Preferisco leggere e scrivere il secondo, ma ricordo di aver letto in codice completo che fare cose del genere è negativo per la manutenibilità.

Questo perché stai facendo affidamento sulla lingua per non valutare la seconda parte ifse la prima parte è falsa e non tutte le lingue lo fanno. (La seconda parte genererà un'eccezione se valutata con un null byteArrayVariable.)

Non so se sia davvero qualcosa di cui preoccuparsi o meno, e vorrei un feedback generale sulla domanda.

Grazie.


1
La prima forma ha il rischio di far penzolare l'altra
JBR Wilkinson,

solo curioso, con che lingua stai lavorando?
STW

@STW: usiamo C # e abbiamo del codice legacy in Delphi.
Vaccano,

2
buono a sapersi. Non ho dimestichezza con Delphi, ma in C # si può essere fiduciosi nelle &&e ||operatori che effettuano la valutazione di corto circuito. Passare da una lingua all'altra di solito ha dei trucchi, ed è bene essere fermi nel fare revisioni del codice per aiutare a capire quei piccoli problemi.
STW,

Mentre con molte risposte qui trovo il secondo più leggibile, il primo è molto più facile da eseguire il debug. In questo caso, userò ancora il secondo perché stai controllando le cose per eseguire il debug. Ma fai attenzione in generale.
Magus

Risposte:


30

Penso che la seconda forma vada bene e rappresenti anche più chiaramente ciò che stai cercando di fare. Lo dici tu...

Ricordo di aver letto nel codice completo che fare cose del genere è negativo per la manutenibilità. Questo perché stai facendo affidamento sulla lingua per non valutare la seconda parte del se la prima parte è falsa e non tutte le lingue lo fanno.

Non importa se tutte le lingue lo fanno. Stai scrivendo in una lingua particolare, quindi importa solo se quella lingua lo fa. Altrimenti, stai essenzialmente dicendo che non dovresti usare le funzionalità di una determinata lingua perché altre lingue potrebbero non supportare quelle funzionalità, e questo è solo stupido.


12
Concordato. Perché mai dovrei preoccuparmi se il mio codice funzionerà allo stesso modo se copiato in un'altra lingua?
Tim Goodman,

16

Sono sorpreso che nessuno l'abbia menzionato (quindi spero di non aver letto male la domanda) - ma entrambi i tipi di schifo!

La migliore alternativa sarebbe quella di utilizzare le uscite anticipate (inserendo una dichiarazione di ritorno all'inizio del codice se nessun altro codice deve essere eseguito). Questo presuppone che il tuo codice sia relativamente ben riformulato e che ogni metodo abbia uno scopo conciso.

A quel punto faresti qualcosa del tipo:

if ((byteArrayVariable == null) || (byteArrayVariable.Length != 0)) {
  return; // nothing to be done, leaving
}

// Conditions met, do something

Potrebbe assomigliare molto alla convalida - ed è esattamente quello che è. Convalida che le condizioni sono state soddisfatte per il metodo di fare la sua cosa - entrambe le opzioni che hai offerto nascondevano la logica effettiva all'interno dei controlli pre-condizione.

Se il tuo codice è un sacco di spaghetti (metodi luccicanti, molti if annidati con logica sparsa tra loro, ecc.) Allora dovresti concentrarti sul problema più grande di dare forma al tuo codice prima dei problemi stilistici più piccoli. A seconda dell'ambiente e della gravità del disordine, esistono alcuni strumenti utili (ad esempio Visual Studio ha una funzione di refactoring "Metodo di estrazione").


Come menziona Jim, c'è ancora spazio per il dibattito su come strutturare l'uscita anticipata. L'alternativa a quanto sopra sarebbe:

if (byteArrayVariable == null) 
  return; // nothing to be done, leaving

if (byteArrayVariable.Length != 0)
  return;

// Conditions met, do something

1
Concordo con il ritorno anziché con la buona logica, ma le stesse persone useranno lo stesso argomento sull'uso di || quanto a &&.
MIA,

14

Non perdere tempo cercando di intrappolare per ogni non questa condizione. Se riesci a squalificare i dati o le condizioni, fallo al più presto.

if (byteArrayVariable == null) Exit;
if (byteArrayVariable.Length == 0) Exit;
// do something

Ciò consente di adattare il codice molto più facilmente per nuove condizioni

if (byteArrayVariable == null) Exit;
if (byteArrayVariable.Length == 0) Exit;
if (NewCondition == false) Exit;
if (NewerCondition == true) Exit;
// do something

2
+1 Yup, yup, yup. La logica così semplice non dovrebbe comportare un codice difficile. Il tuo intestino (di chi chiede) dovrebbe dirti questo.
Giobbe

11

Il tuo esempio è l'esempio perfetto del perché i nidificati ifssono un cattivo approccio per la maggior parte delle lingue che supportano correttamente il confronto booleano. La nidificazione ifscrea una situazione in cui la tua intenzione non è affatto chiara. È necessario che il secondo ifsegua immediatamente il primo? O è solo perché non hai ancora effettuato un'altra operazione?

if ((byteArrayVariable != null) && (byteArrayVariable.Length != 0))
  //Do something with byteArrayVariable 

In questo caso, questi devono praticamente stare insieme. Stanno fungendo da guardia per assicurarsi che non si esegua un'operazione che comporterebbe un'eccezione. Usando nidificato ifs, si lascia all'interpretazione della persona che ti segue se i due rappresentano una guardia in due parti o qualche altra logica di ramificazione. Combinandoli in un singolo lo ifdico. È molto più difficile intrufolarsi in un'operazione dove non dovrebbe essere.

Favorirò sempre una chiara comunicazione della mia intenzione sulla stupida affermazione di qualcuno che "non funziona in tutte le lingue". Indovina cosa ... thrownon funziona neanche in tutte le lingue, significa che non dovrei usarlo durante la codifica in C # o Java e dovrei invece fare affidamento sui valori di ritorno per segnalare quando si è verificato un problema?

Detto questo, è molto più leggibile invertirlo e tornare al di fuori del metodo se uno dei due non è soddisfatto come dice STW nella loro risposta.


Più di due condizioni potrebbero giustificare la propria funzione che restituisce un valore nullo.
Giobbe

3

In questo caso le tue due clausole sono direttamente correlate, quindi è preferibile la seconda forma.

Se non fossero correlati (ad esempio una serie di clausole di guardia su condizioni diverse) preferirei la prima forma (o riforterebbe un metodo con un nome che rappresenta ciò che la condizione composta rappresenta effettivamente).


1

Se lavori in Delphi, il primo modo è, in generale, più sicuro. (Per l'esempio dato, non c'è molto da guadagnare dall'uso di if nidificati.)

Delphi ti consente di specificare se valutare o meno le espressioni booleane o "cortocircuito", tramite gli switch del compilatore. Il modo n. 1 (if nidificati) consente di garantire che la seconda clausola venga eseguita se e solo se (e rigorosamente dopo ) la prima clausola viene valutata come vera.


1
In 23 anni di sviluppo di Delphi non ho mai cambiato l'opzione "Completa valutazione booleana". Se spedissi un codice di spedizione altrove, metterei {$ BOOLEVAL OFF} o {$ B-} nell'unità.
Gerry,

Neanche io. Uso sempre anche il controllo del range ... ma altre persone no. E non dovrebbe fare la differenza se non forse dal punto di vista delle prestazioni perché non dovresti usare gli effetti collaterali nelle funzioni usate nelle espressioni booleane. Sono sicuro che qualcuno là fuori lo fa!
Frank Shearar,

@Gerry: d'accordo. L'unico motivo per cui potresti desiderare una valutazione completa sono gli effetti collaterali - e gli effetti collaterali in un condizionale sono una cattiva idea!
Loren Pechtel,

Rileggi il mio commento 23 anni dovrebbero essere 13! Non ho un Tardis
Gerry il

1

Il secondo stile può ritorcersi contro in VBA poiché se il primo test è se l'oggetto generato, farà comunque il secondo test ed errore. Ho appena preso l'abitudine in VBA quando ho a che fare con la verifica degli oggetti per usare sempre il primo metodo.


2
Questo perché VBA è un linguaggio terribile
Falmarri,

@ Falmarri, ha delle cose terribili (e alcune cose buone). Riparerei un sacco di cose se avessi i miei druthers.

2
La mancanza di una valutazione booleana del cortocircuito è più di una cosa legacy. Non ho idea del perché non abbiano iniziato con questo. La "correzione" VB.NET di OrElse e AndAlso è un po 'fastidiosa, ma probabilmente l'unica opzione per affrontarla senza interrompere la compatibilità con le versioni precedenti.
JohnFx,

1

Il secondo modulo è più leggibile, soprattutto se si utilizzano blocchi {} attorno a ciascuna clausola, poiché il primo modulo porterà a blocchi {} nidificati e a più livelli di rientro, il che può essere un problema da leggere (è necessario contare gli spazi di rientro per capire dove finisce ogni blocco).


0

Li trovo entrambi leggibili e non accetto l'argomento secondo cui dovresti preoccuparti della manutenibilità del codice se dovessi cambiare lingua.

In alcune lingue la seconda opzione funziona meglio, quindi sarebbe la scelta più chiara.


0

Sono con te; Lo faccio nel secondo modo. Per me, è logicamente più chiaro. La preoccupazione che alcune lingue eseguiranno la seconda parte, anche se la prima valuta falsa, mi sembra un po 'sciocca, dato che nella mia vita non ho mai scritto un programma che funzionasse in "alcune lingue"; il mio codice sembra sempre esistere in una lingua specifica, che o può gestire quella costruzione o no. (E se non sono sicuro che la lingua specifica sia in grado di gestirla, è qualcosa che devo davvero imparare, no?)

Nella mia mente, il pericolo con il primo modo di farlo è che mi rende vulnerabile all'inserimento accidentale di codice al di fuori di quel ifblocco nidificato quando non intendevo farlo. Il che, è vero, non è certo una grande preoccupazione, ma sembra più ragionevole che preoccuparsi se il mio codice è indipendente dalla lingua.


0

Preferisco la seconda opzione, perché è più leggibile.

Se la logica che stai implementando è più complessa (verificare che una stringa non sia nulla e che non sia vuota è solo un esempio), puoi fare un utile refactoring: estrarre una funzione booleana. Sto con @mipadi sull'osservazione "Codice completo" ("non importa se tutte le lingue lo fanno ...") Se il lettore del tuo codice non è interessato a guardare all'interno della funzione estratta, allora l'ordine in cui vengono valutati operandi booleani diventa una questione controversa per loro.


0
if ((byteArrayVariable != null)
 && (byteArrayVariable.Length != 0)) {
    //Do something with byteArrayVariable 

ma questa è fondamentalmente la seconda opzione.

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.