L'uso della programmazione ELSE è errato? [chiuso]


18

Mi sono imbattuto spesso in bug causati dall'uso del ELSEcostrutto. Un primo esempio è qualcosa sulla falsariga di:

If (passwordCheck() == false){
    displayMessage();
}else{
    letThemIn();
}

Per me questo urla un problema di sicurezza. So che passwordCheck è probabilmente un valore booleano, ma non metterei la sicurezza delle mie applicazioni su di esso. Cosa succederebbe se fosse una stringa, int etc?

Di solito cerco di evitare di usare ELSE, e invece optare per due istruzioni IF completamente separate per verificare ciò che mi aspetto. Qualsiasi altra cosa viene ignorata o viene gestita in modo specifico.

Sicuramente questo è un modo migliore per prevenire bug / problemi di sicurezza che accedono alla tua app.

Come fate voi ragazzi a farlo?


3
Qual è il problema di sicurezza per te? Cosa significa "passwordCheck"? C'è stato un controllo della password? È necessario un controllo della password? L'utente è passato? L'utente non è riuscito a inserire la password corretta?
LennyProgrammers,

20
I know that passwordCheck is likely to be a boolean...Cosa intendi? In qualsiasi linguaggio tipografico. passwordChecksarà qualunque cosa tu voglia che sia.
Bobby,

31
Penso che le cattive pratiche di rientro comportino più errori rispetto all'uso delle elsedichiarazioni ...
gablin,

15
Sembra strano. In primo luogo, ti lamenti del possibile tipo di restituzione che passwordCheck()potrebbe non essere booleano (che potrebbe essere una preoccupazione ragionevole), e poi la biasimi else? Non vedo quali problemi le elsecause.
David Thornley,

8
mmm, penso che chiedere se usare altro sia una cattiva programmazione è una cattiva programmazione
Muad'Dib il

Risposte:


90

Il elseblocco deve sempre consistere in ciò che si desidera sia il comportamento predefinito.

Non è necessario evitarli, basta fare attenzione a usarli in modo appropriato.

Nel tuo esempio, lo stato predefinito dovrebbe essere di non consentire l'accesso. Un piccolo refactoring ti lascia con:

If (passwordCheck)
{
   letThemIn();
}
else
{
   displayMessage();
}

cioè se il controllo della password funziona, falli entrare, altrimenti è sempre valido mostrare qualche messaggio di errore.

Ovviamente puoi aggiungere ulteriori controlli alla tua logica usando istruzioni else ifpiuttosto che completamente separate if.


2
@ dave.b nel contesto dell'esempio, suppongo che sarebbe un problema di sicurezza, ma se questo è dappertutto nella base di codice che stai guardando, è più un segno di chi lo ha scritto che ha bisogno di un po 'di più pratica :)
RYFN

9
Qualcuno può approfondire l'aspetto della sicurezza di questo? if (! qualcosa) {do a} else {do ​​b} contro if (qualcosa) {do b} else {do ​​a} è logicamente equivalente no? Sto cercando di capire qual è la differenza in termini di sicurezza di questo?
Chris,

11
@ Chris: Penso che l'OP usi un linguaggio debolmente tipizzato. Pertanto passwordCheckpotrebbe essere qualsiasi cosa, fe null, che renderebbe passwordCheck == falseper falsee avrebbe l'utente permettono di login a causa di un errore interno.
Bobby,

1
@Chris, la mia comprensione era che lo stato predefinito era consentire l'accesso, il che non è necessariamente consigliabile.
RYFN,

3
Sì, questo dipende molto dalla lingua. In C #, dove ifrichiede a bool, le variabili devono essere definitivamente assegnate, tutti i percorsi devono restituire un valore, ecc., Non riesco a pensare a nessun motivo dell'ordine ife elseconterebbe oltre alla leggibilità. Cioè, if(a){b();}{c();}dovrebbe essere equivalente a if(!a){c();{b();}. In JavaScript, d'altra parte, devi essere consapevole che passwordCheckpotrebbe essere undefined, ecc.
Tim Goodman,

57

No, non c'è niente di sbagliato in ELSE. ELSEnon è il nuovo GOTO. In effetti, l'uso di due IFs invece di ELSEpuò portare a diversi problemi.

Esempio uno:

if (this(is(a(very())==complex(check())))) {
   doSomething();
}

if (!this(is(a(very())==complex(check())))) {
   doTheOtherThing();
}

Vedi il copia-incolla? Aspetta solo il giorno in cui cambi uno e dimentichi l'altro.

Esempio due:

foreach(x in y) {
  if (first_time) {
    first_time = false;
    doSomething();
  }

  if (!first_time) {
    doTheOtherThing();
  }
}

Come puoi vedere, il secondo IFverrà eseguito anche per il primo elemento, poiché la condizione è già cambiata. Nei programmi del mondo reale, tali bug sono più difficili da individuare.


12
Sono d'accordo con questo. Copia-incolla ifun'istruzione e invertendo la sua espressione booleana solo per evitare un else- ora che è una cattiva programmazione! Non solo stai duplicando il codice ( cattiva programmazione), stai anche rallentando le prestazioni (se il controllo è davvero complicato, ora lo stai facendo due volte - ba-- uh, beh, sai cosa dicono di ottimizzazioni premature al giorno d'oggi ... non è una buona programmazione!).
gablin

A dire il vero, se Check dovrebbe essere in un metodo a sé stante per restituire true / false
billy.bob

Buoni esempi, non dimentichiamoci delle prestazioni dell'utilizzo di 2 se i controlli rispetto a uno se accoppiato con un altro. Suppongo che nella maggior parte delle lingue l'uso di un if / else avrà prestazioni migliori rispetto all'utilizzo di due istruzioni if ​​con una che utilizza un non sulla condizione.
Chris,

1
dave.b: certo, ma potrebbe iniziare in modo semplice e crescere lentamente; il controllo complesso nel mio esempio è qui per rendere il problema più evidente.
user281377

3
Sì, e non dimenticare che le condizioni possono avere effetti collaterali nella maggior parte delle lingue, e se ci sono funzioni nelle condizioni che non puoi davvero vedere guardando.
David Thornley,

18

C'è sempre un ELSE. Se scrivi

if(foo)
  bar();

in realtà scrivi

if(foo)
{
  bar();
}
else
{
   // do nothing
}

Qualunque cosa tu metta nel percorso ELSE è la tua responsabilità.


7
-1. Questa risposta è troppo ridicola. Non sto dicendo che non è vero. Manca solo il punto. Ciò che la compilazione genera dal tuo codice non ha nulla a che fare con le pratiche di codifica e i bug che scrivi

3
La domanda era "Sta usando una programmazione errata ELSE?". La mia risposta è: puoi fingere che non sia lì, ma non evitarlo.
LennyProgrammers,

5
quale lingua ? in Java l'altro non è nel bytecode: P
IAdapter

@ acidzombie24 - è una buona pratica considerare qual è l '"altro" da qualsiasi domanda. A volte è solo - fai tutto il resto nella fucntion, ma dimostra che ci hai pensato
Martin Beckett

14

Personalmente tendo ad evitare elseil più possibile, ma non è per alcun problema di sicurezza .

Durante la lettura del codice, le istruzioni nidificate rendono più difficile seguire la logica, poiché è necessario ricordare quale serie di condizioni porterà lì. Per questo motivo, sono un grande fan dell'uscita anticipata:

if (checkPassword != OK) { displayMessage(); return; }

letThemIn();

Ciò vale anche per fore whilein cui userò loop continuee breakogni volta evita un livello di rientro.

Chris Lattner lo dice meglio di me negli standard di codifica LLVM .


Sono d'accordo. "else" crea una "forcella" mentale e noi umani siamo creature sequenziali.
user187291

Cordiali saluti, si chiama una condizione di guardia
CaffGeek

@Chad: ah grazie, sempre bello avere un nome per le cose :)
Matthieu M.

1
+1. Questa è l'unica risposta che posso sopportare con un punteggio> 1. *writes an answer*

Non cerco di evitare altro in quanto tale, ma penso di essere d'accordo con quello che scrivi. Il punto è che la cosa che stai testando dovrebbe essere l'eccezione e non il caso normale ( stackoverflow.com/questions/114342/… ). Qui l'errore di autenticazione è l'eccezione e (solo) che dovrebbe essere testato.
hlovdal

5

Quindi sostituiscili,

If (passwordCheck == true)
{
     letThemIn();
}
else
{
     displayMessage();
}

21
Che ne dici If ((passwordCheck == true) == true)? :-)
Ippopotamo il

1
Bene, è il mio stile, per migliorare la leggibilità. Puoi scrivere if (! Value) ma preferisco if (value! = True)
Ahmet Kakıcı

7
Non migliora la leggibilità. Aggiunge solo rumore. Ancora peggio sono i programmatori che scrivono nidificati se costruiscono solo perché hanno paura degli operatori booleani.
ak2,

3
Se il nome della variabile è descrittivo, non c'è motivo per cui: if (someBooleanValue == true) dovrebbe essere necessario. Sarebbe meglio: if (validPassword) {...
Mark Freedman,

Bene, ho appena sostituito i blocchi di codice all'interno della domanda. Se leggi il mio primo commento, ho detto che preferisco usare if (value! = True) anziché if (! Value). Spero che tu possa vedere la differenza tra if (valore) e if (! Valore). Volevo dire che non uso l'operatore di complemento. Altrimenti hai ragione, vero significa vero.
Ahmet Kakıcı,

3

Come Matthieu M., preferisco l'uscita anticipata a blocchi nidificati in altro modo ... Illustra una programmazione difensiva (se in cattive condizioni, nessun punto per continuare). Molte persone non saranno d'accordo con noi, preferendo un punto di uscita unico; non è il punto del dibattito (penso).

Ora, certamente lo uso elsequando ha senso, in particolare per alternative semplici e brevi. Come detto, la duplicazione del test è una perdita di tempo (programmatore e CPU), fonte di confusione e, in seguito, di bug (quando si cambia uno, non l'altro).
A volte, aggiungo un commento sulla elseparte, ricordando quale fosse la condizione (in particolare se la ifparte è lunga, ad es. Nel codice legacy) o qual è l'alternativa.

Si noti che alcuni sostenitori estremi della programmazione funzionale propongono di sbarazzarsi del tutto if, a favore della corrispondenza dei modelli ... Un po 'troppo estremo per i miei gusti. :-)


1
come nota a margine: se hai un costrutto if nel tuo linguaggio funzionale puro, allora devi davvero avere un altro. Ogni espressione deve restituire qualcosa!
tokland,

2

Non c'è niente di sbagliato nell'utilizzo di ELSE. Tuttavia, può portare a un codice eccessivamente complesso, difficile da leggere e comprendere. Potrebbe indicare un cattivo design. Indica certamente ulteriori casi d'uso che dovranno essere testati.

Prova a rimuovere gli ELSE se puoi - ma non essere paranoico al riguardo. Steve McConnell chiama questo codice di linea retta in Codice completo. Cioè c'è un semplice percorso chiaro attraverso il codice.

Approcci da provare per il tuo problema specifico:

  • usa il polimorfismo. Al limite del sistema convalidare le credenziali di sicurezza dell'utente. Se sono legittimi, restituisci un oggetto sessione - con accesso alle parti pertinenti del sistema o genera un'eccezione. Tuttavia, ciò potrebbe rendere il sistema più complesso. Quindi decidi cosa è più facile da capire e mantenere.

In generale, quanto segue può aiutare a ridurre gli ELSE nel codice:

  • buoni requisiti possono ridurre la necessità di tali decisioni nel codice. Potrebbe non essere necessario implementare il caso d'uso (altro).
  • design più chiaro. Massima coesione e riduzione al minimo dell'accoppiamento. Ciò garantisce che i componenti non stiano duplicando le decisioni prese in altri componenti.
  • gestione delle eccezioni per gestire i casi di errore.
  • polimorfismo (vedi esempio sopra).
  • dichiarazioni switch - questi sono ELSE glorificati ma sono migliori in determinate situazioni.

2
Includerei anche "Sposta complessi controlli booleani in una funzione separata".
gablin

2

La tua supposizione sul fatto che il codice sia una perdita di sicurezza può o non può essere vera a seconda della lingua che stai utilizzando. Nel codice C potrebbe essere un problema (soprattutto perché in C un booleano è solo un int che è diverso da zero o zero) - ma nei linguaggi più tipicamente tipizzati (ad esempio controllo del tipo di runtime) se la passwordCheckvariabile è stata dichiarata come booleana, non c'è modo di assegnargli qualcos'altro. In effetti, tutto in un ifpredicato deve risolversi in un valore booleano, sia che si utilizzino gli operatori booleani sia che si utilizzi semplicemente il valore. Se sei riuscito ad avere un altro tipo di oggetto associato al passwordCheckruntime genererebbe un tipo di eccezione di cast illegale.

I costrutti if / else semplici sono molto più facili da leggere rispetto ai costrutti if / if e sono meno inclini a problemi involontari se qualcuno tenta di capovolgere il costrutto. Facciamo lo stesso esempio per un secondo:

if(passwordCheck == false) {
    denyAccess();
}

if(passwordCheck) {
    letThemIn();
}

Il significato delle clausole reciprocamente esclusive che si desidera eseguire sopra è perso. Questo è ciò che trasmette il costrutto if / else. Due rami di esecuzione reciprocamente esclusivi, in cui uno di essi funzionerà sempre. Questa è una parte importante della sicurezza: garantire che non ci sia modo di farlo letThemIndopo aver chiamato denyAccess.

Ai fini della chiarezza del codice e allo scopo di garantire che le sezioni critiche siano maggiormente protette, dovrebbero trovarsi all'interno della clausola primaria (la ifparte). Il comportamento predefinito non conforme dovrebbe essere nella clausola alternativa (la elseparte). Per esempio:

if(passwordCheck) {
    letThemIn();
} else {
    denyAccess();
}

NOTA: lavorando con lingue diverse, ho sviluppato un habbit di codifica che aiuta a evitare la domanda "che cosa succede se si tratta di una stringa?" In sostanza, è quello di mettere la costante al primo posto nell'espressione booleana. Ad esempio, invece di verificare, passwordCheck == falsesto verificando false == passwordCheck. Questo evita anche il problema di assegnazione accidentale possibile in C ++. Usando questo approccio, il compilatore si lamenterà se scrivo =invece di ==. In linguaggi come Java e C #, il compilatore tratterà l'assegnazione nella clausola if come un errore, ma C ++ lo accetterà felicemente. Ecco perché tendo anche a fare il controllo nullo con il nullprimo.

Se cambi sistematicamente le lingue, posizionare la costante per prima è molto utile. Tuttavia, nel mio team è contrario allo standard di codifica e il compilatore rileva comunque questi problemi. Può essere un duro habbit da rompere.


1

Dire che usare elsequando si programma è male è come dire che usare otherwisequando si parla è male.

Certo, possono entrambi essere usati in modi sbagliati, ma ciò non significa che debbano essere evitati solo perché hai fatto un errore che li ha inclusi. Non sarei sorpreso se molti bug dipendessero da un defaultcaso mancante in una switchdichiarazione.


1

Pensa Elsea una lista bianca del tuo flusso di applicazioni. Verifichi le condizioni che DOVREBBE consentire al flusso dell'applicazione di continuare e, se non vengono rispettate, Elsevengono eseguite per risolvere il problema, interrompere l'esecuzione dell'applicazione o qualcosa di simile.

Else di per sé non è male, ma se lo usi male, puoi vedere effetti indesiderati.

Inoltre, per quanto riguarda la tua dichiarazione in merito

"So che passwordCheck è probabilmente un valore booleano, ma non metterei la sicurezza delle mie applicazioni su di esso."

Per i metodi sviluppati, restituire SEMPRE un tipo di dati. Sebbene PHP Core sia pieno di codice che restituisce due o più tipi di dati, questa è una cattiva pratica in quanto fa congetture sulle chiamate di funzione. Se devi restituire più di un tipo di dati, prendi in considerazione la possibilità di generare un'eccezione (trovo che questo sia spesso il motivo per cui vorrei restituire un altro tipo di dati - qualcosa è andato in modo orribile, orribilmente sbagliato), o prendere in considerazione la ristrutturazione del tuo codice in modo da poter restituisce solo un tipo di dati.


Non sapevo che esistessero lingue che restituivano più di un tipo di dati! Ti riferisci alle funzioni polimorfiche? Credo che ogni volta che sovraccarico una funzione abbia sempre restituito lo stesso tipo di dati, anche se può richiedere argomenti diversi.
Michael K,

1
Nei linguaggi tipicamente diffusi, e specialmente in PHP, le funzioni possono restituire più di un tipo di dati. es: stristr- "Restituisce la sottostringa abbinata. Se l'ago non viene trovato, restituisce FALSO"
Craige

@Michael, Una funzione PHP può restituire qualsiasi cosa tu voglia. Non ci sono restrizioni sul tipo di dati. La mia funzione più complicata restituisce true / false / null, ma non c'è nulla (tranne il buonsenso) che ti impedisce di scrivere una funzione che restituisca true / integer / null / string / float / array.
TRiG

Interessante. Non ho mai lavorato con PHP. Grazie per la spiegazione però - probabilmente mi aiuti in futuro! un po 'come Javascript allora?
Michael K,

@Michael - Abbastanza simile a Javascript.
Craige,

1

Prima di tutto. LOL! Non c'è alcun motivo per evitare altrimenti. Non è una cattiva pratica in alcun modo, forma o forma.

Semmai il codice dovrebbe essere

if(!IsLoggedIn) { ShowBadLoginOrNoAccessPage(); return }

Non ci sono due se e non ha altro. Questo è ciò che faccio in tutte le mie app tranne in una in cui lancio un'eccezione. L'eccezione è rilevata nella mia funzione che controlla l'URL per la pagina corretta da mostrare (o in alternativa posso mettere il catch / check nella funzione di errore asp.net). Stampa una pagina generica che dice di non autorizzare o qualsiasi messaggio che uso nell'eccezione (controllo sempre il tipo di eccezione e imposto il codice di stato http).

-Modifica- come mostrato nell'esempio di ammoQ due ifs è ridicolo. In realtà il resto è altrettanto buono o meglio di un caso. Se qualcosa deve essere evitato (anche se personalmente non lo faccio. Ma uso return e break molto) come è stato detto, più percorsi di codice aumentano la probabilità di bug. Vedi Complessità ciclomatica

-Modifica 2- Se sei preoccupato per l'utilizzo dell'altro /. Noterò anche che la mia preferenza è quella di mettere in cima il blocco di codice più corto come

if(cond == false) {
    a()
    b()
    c()
    onetwothree()
}
else
{
    a()
    b()
    c()
    more()
    onetwothree()
    code()
    longer()
}

Piuttosto che

if(cond) 
{
    a()
    b()
    c()
    more()
    onetwothree()
    code()
    longer()
}
else
{
    a()
    b()
    c()
    onetwothree()
}

0

Mi piace impostare un valore predefinito prima dei condizionali quando posso. Mi sembra che sia un po 'più facile da leggere e un po' più esplicito, ma questa è solo una preferenza. Ho la tendenza a cercare di evitare condizioni negative nel mio codice. Non sono un grande fan della ricerca di! Foo o false == foo e penso che il resto sia un po 'l'equivalente condizionale di un negativo.

foo = bar;

if ('fubar' == baz) {
    foo = baz;
}

invece di ...

if ('fubar' == baz) {
    foo = baz;
} else {
    foo = bar;
}

Il precedente blocco di codice mi sembra più semplice da leggere. Mi sembra più naturale avere una sorta di paranoia scettica sul mio codice. L'impostazione di un valore predefinito indipendentemente da qualsiasi condizione mi fa sentire a mio agio: P


0

Direi che l'uso della logica di ramificazione di qualsiasi tipo dovrebbe essere evitato il più possibile. Sebbene non ci sia nulla di sbagliato in ELSE o IF, ci sono molti modi per scrivere codice per ridurre al minimo la necessità di utilizzare qualsiasi logica di ramificazione. Non sto dicendo che la logica di ramificazione possa essere completamente eliminata - sarà necessaria in alcuni punti - ma è possibile riformattare il codice per eliminarne una buona parte. Nella maggior parte dei casi ciò migliorerà l'intelligibilità e l'accuratezza del codice.

Ad esempio, anche gli operatori ternari sono generalmente buoni candidati:

If VIP Then 
  Discount = .25
Else
  Discount = 0
End If
Total = (1 - Discount) * Total

Utilizzando un approccio ternario:

Discount = VIP ? .25 : 0
Total = (1 - Discount) * Total

Gli operatori ternari spostano la ramificazione verso destra in un buon modo.

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.