2009-06-20 4 views
9
procedure MyProc(Eval: Boolean); 
begin  
    if not Eval then 
     Exit; 
    /* do stuff */ 
    /* do more stuff */ 
end; 

OQual è l'opzione migliore?

procedure MyProc(Eval: Boolean); 
begin 
    if Eval then 
     begin 
     /* do stuff */ 
     /* do more stuff */ 
     end; 

    /* no Exit needed, but now we got what I think unpleasing code: 
    having a indentation level and a begin-end statement */ 
end; 
+9

Perché non si limitano a fare cose e altre cose nella propria funzione e quindi si mette la frase if sulla chiamata alla funzione? O mi sto perdendo qualcosa qui? –

+0

Tomas, avresti potuto guadagnare punti ... Questo commento è buono come una risposta. Ed è già stato messo in svantaggio da pochi ... –

+0

Thaks Thomas per la sua formazione! Bene, so che rompe la coesione, ma diciamo che i due stufs gestiscono due domini molto diversi, quindi non possiamo bloccarli in una nuova funzione. –

risposta

1

Posso solo fare un appello che se si utilizza il secondo modulo, non si aggiunge un livello di induzione gratuito ectra. Così insrtead di:

procedure MyProc(Eval: Boolean); 
begin 
    if Eval then 
     begin 
     /* do stuff */ 
     /* do more stuff */ 
     end; 

    /* no Exit needed, but now we got what I think unpleasing code: 
    having a indentation level and a begin-end statement */ 
end; 

dicono:

procedure MyProc(Eval: Boolean); 
begin 
    if Eval then 
    begin 
     /* do stuff */ 
     /* do more stuff */ 
    end; 

    /* no Exit needed, but now we got what I think unpleasing code: 
    having a indentation level and a begin-end statement */ 
end; 
+0

Nessuna critica era intesa. Il punto è che molte persone usano livelli di indentazione inutili, ed è un grande dolore. C'è uno stile di imndenetazione C molto orribile che è in qualche modo simile al primo esempio e deve essere calpestato. –

+0

@Neil Butterworth: Wow man, è stato un errore di formatura, mi dispiace per quello. Non sono completamente abile con l'editor di risposte e con il mio inglese, ma in ogni caso dobbiamo identificare il/* fare cose */e/* fare più cose * –

16

ci sono casi in cui entrambi i metodi è appropriata. In genere, tuttavia, il primo offre un codice più leggibile e un flusso di controllo migliore. Non ho molta familiarità con la programmazione Delphi, ma in C# cerco sempre di usare il primo quando possibile. A giudicare da ciò, non vedo ragioni per cui l'approccio dovrebbe differire in Delphi.

Per elencare alcuni dei vantaggi:

  • Non c'è bisogno di rientrare codice successivo.
  • Più facile da estendere a più condizioni. (Basta aggiungere ulteriori istruzioni if ​​piuttosto che operatori logici, il che rende le cose più chiare IMO.)
  • L'idea che si sta "scartando" il metodo piuttosto che optare è più esteticamente gradevole, dal momento che il seguente codice deve essere eseguito in il caso "normale".

Tuttavia, ci sono situazioni in cui la seconda opzione è più appropriata. In particolare, quando il metodo deve essere suddiviso in sottosezioni (anche se questo è spesso un segno che è necessario refactoring).

10

Penso che sia una questione di preferenza. Tuttavia, se hai un numero di controlli che devi eseguire prima di eseguire il "vero lavoro", il primo modulo (IMO) sembra più ordinato ed è più facile seguire il flusso. Ad esempio:

procedure MyProc(Eval: Boolean); 
begin 
    if not Eval then 
     Exit; 
    if not Eval2 then 
     Exit; 
    /* do stuff */ 
    /* do more stuff */ 
end; 

v.s.

procedure MyProc(Eval: Boolean); 
begin 
    if Eval then 
    begin 
     if Eval2 then 
     begin 
      /* do stuff */ 
      /* do more stuff */ 
     end; 
    end; 
end; 
+0

Il secondo esempio non è il migliore, dal momento che è possibile utilizzare gli operatori logici per semplificarlo. – Noldorin

+0

@Noldorin: Punto giusto, ma non volevo complicare eccessivamente l'esempio. Tuttavia puoi immaginare scenari in cui "Eval2" è una sequenza di istruzioni più complessa che non può essere banalmente combinata con il controllo "Eval". – DaveR

+0

Sì, è abbastanza giusto. Sono ancora molto d'accordo con il vostro punto generale. – Noldorin

4

ci sono quelli che sosterranno si dovrebbe avere un unico punto di uscita nella funzione, ma io non sono in quel campo.

Uso spesso i multi-return e non influisce sulla leggibilità di funzioni brevi e ben congegnate, quindi direi semplicemente andare per quello che pensi sia il più leggibile.

Ho avuto codice refactoring persone che ho scritto per seguire le "regole" ed è quasi sempre finito con un impaccio di codice illeggibile dovuto principalmente all'indentazione eccessiva. Dovrebbero averlo lasciato o fatto correttamente, trasformandolo in più funzioni.

Un fastidio particolare che vedo è del calibro di "else" in:

if (some condition) 
    return false; 
else 
    keep going; 

Pensano il flusso di controllo è in qualche modo sta per fuggire dalla clausola di "then"?

+0

Concordo sul fatto che lo stile del codice è spesso esagerato. –

3

Io uso molto il primo modulo. È una programmazione un po 'letterale delle condizioni pre, specialmente perché quando il codice viene inserito tra i controlli effettivi nel secondo esempio, le condizioni esatte sono meno chiare.

Io provo a limitare l'uso di EXIT alla prima parte di controllo della condizione della procedura, tuttavia, per evitare troppi spaghetti, ma penso che la prima forma sia più pulita dei cerchi per saltare per conservare quell'unico punto di uscita.

E sembra per essere utilizzato molto, considerando il fatto che l'uscita (returnValue) è stata aggiunta in D2009 (e FPC averlo per un decennio)

0

La realtà è che questo è troppo semplice un esempio a prevaricare su . Sceglierai il tuo approccio in base alla tua convenzione esistente e questo esempio è troppo semplice per prendere una decisione sulla convenzione. La cosa più interessante è che cosa fare con più opzioni get-out-now e istruzioni IF annidate: è una vera area grigia.

Allora, qual è la mia convention? Ah ... sono pragmatico, non ho regole. Se è possibile saltare la finestra nelle prime righe, la prendo, perché non devo ricordare ogni volta che modifico il codice che alcune "chiamate di funzioni non corrisposte" potrebbero ancora essere attive in qualsiasi momento durante la funzione.

Nel corso del tempo questa funzione sarà modellata e impastata da bug e miglioramenti. È più probabile che tu introduca un nuovo bug con quest'ultimo approccio rispetto al primo, perché l'uscita rapida è ovvia all'inizio, nella tua faccia, e non devi preoccuparti di aver "dimenticato" 50 righe giù per la funzione . Nella mia esperienza introduttiva di bug, ovviamente.

Questa è una chiamata più complicata se si impostano le cose e si devono abbatterle perché saltare fuori può costringerti a manipolare 17 stati di gatto di Schrodinger nella tua testa quando apporti dei cambiamenti.

+0

Penso che una dozzina di istruzioni nidificate di solito significhi codice privo di coesione, quindi abbiamo dovuto ricattarlo, almeno per essere più leggibile. –

+1

"una chiamata più complicata se si impostano le cose e bisogna abbatterle perché saltare fuori può costringerti a ..." - no, non proprio. Poiché le eccezioni possono causare comunque l'uscita anticipata del codice, è necessario codificare il tearing down utilizzando infine il conteggio dei riferimenti automatico o finale. Se funziona correttamente con le eccezioni, funzionerà anche con exit. – mghie

+0

mghie - true solo se l'uscita tramite un'eccezione e non mi piace usare eccezioni per modellare ogni uscita di codice, inoltre il mio "abbattere" non significa necessariamente oggetti. dipende anche se la tua lingua ha delle eccezioni - ad es. gli utenti di vba sono un po 'troppo brevi su questo. So che la domanda è Delphi, ma funziona come una domanda generale che è come mi sono avvicinato. Devo anche lavorare in un altro ambiente che non ha eccezioni e un supporto di funzionalità scadente, quindi mi occupo di tutto questo tempo. Non ho una regola ferrea: implementa solo ciò che sembra giusto al momento. –

0

A mio parere, nessuna è la risposta corretta.

La soluzione corretta sarebbe quella di includere solo le parti "do stuff" e "do more stuff" nella procedura. Quindi, avvolgere la chiamata di procedura in un'istruzione if, solo chiamandola quando necessario.

Se, come dici nel tuo commento, che "fare cose" e "fare più cose" coprono due domini diversi, allora forse vuoi due procedure: una per "fare cose" e un'altra per "fare più cose" . Se esegui entrambe le procedure insieme abbastanza spesso, puoi anche includere una terza procedura che chiama semplicemente le altre due procedure.

+0

@Thomas: ha senso! Ma a volte, abbiamo cose così complesse che sembrano buone per incapsularla in un sacco di piccoli pezzi come procedure, funzioni o "domande", ma d'altra parte sembra peggiore di granularla troppo in profondità, anche se in questo modo rompe la coesione regola. –

+0

Finché il codice non è così granulare che diventa difficile da capire, è perfettamente a posto. Inoltre, una singola funzione non dovrebbe mai eseguire due compiti distinti. –

2

Credo che i punti di uscita meno un segmento di codice ha la meglio così è più facile da leggere e capire. Notare è peggio che eseguire il debug della funzione di linea di 100 di qualcun altro e scoprire che ci sono 12 diverse situazioni disseminate al suo interno che possono indurlo a tornare presto.

+0

@Hardwareguy: Sono pienamente d'accordo con te. Un artefatto di codice deve avere solo un punto di uscita, perché quando le cose vanno male, abbiamo ottenuto un'eccezione. –

3

preferisco:

if not Eval then Exit; 

perché il codice è più pulito in questo modo.