Quali passi e misure posso prendere per prevenire profonde rientranze nel mio codice?
Quali passi e misure posso prendere per prevenire profonde rientranze nel mio codice?
Risposte:
Il rientro profondo di solito non è un problema se ogni funzione / metodo nel tuo programma fa una sola cosa. Occasionalmente, potrebbe essere necessario nidificare i condizionali a pochi livelli di profondità, ma posso onestamente dire di aver scritto solo codice profondamente indentato una manciata di volte in oltre 12 anni di codifica.
La cosa migliore che puoi fare è estrarre i metodi:
int Step1(int state)
{
if (state == 100)
{
return Step2(state);
}
else
{
return Step3(state);
}
}
int Step2(int state)
{
if (state != 100)
{
throw new InvalidStateException(2, state);
}
// ....
}
if
. Portato all'estremo, finirai con uno pseudocodice eseguibile.
else
blocchi non necessari .
Forse potresti prendere in considerazione le clausole di guardia ?
invece di
public void DoSomething(int value){
if (someCondition){
if(someOtherCondition){
if(yetAnotherCondition){
//Finally execute some code
}
}
}
}
Fare
public void DoSomething(int value){
if(!(someCondition && someOtherCondition && yetAnotherCondition)){
return;
//Maybe throw exception if all preconditions must be true
}
//All preconditions are safe execute code
}
Se mai avessi la possibilità, ti consiglio di leggere Codice completo di Steve McConnell. Ha molti consigli su questi argomenti.
http://www.amazon.com/Code-Complete-Practical-Handbook-Construction/dp/0735619670/ref=pd_sim_b_6
Per ulteriori informazioni sulle "clausole di guardia", consultare: https://sourcemaking.com/refactoring/replace-nested-conditional-with-guard-clauses
Invertire la tua if
s.
Invece di:
if (foo != null)
{
something;
something;
if (x)
{
something;
}
something;
}
else
{
boohoo;
}
Scriverei:
if (foo == null)
{
boohoo;
return;
}
something;
something;
if (x)
{
something;
}
something;
Lo stesso vale per if
- else
blocchi. Se else
è più breve / meno nidificato, ripristinalo.
Controlla i valori dei parametri in un unico posto
Controlla tutti i parametri per valori illegali non appena inserisci il tuo metodo, quindi procedi sapendo che sei al sicuro. Rende il codice più leggibile, ma ti salva anche accumulando blocchi condizionali in seguito e diffondendo questi controlli in tutta la subroutine.
If
s all'inizio del codice che interrompe il flusso di esecuzione a causa di una condizione non soddisfatta sono anche note come clausole di salvaguardia , come ha sottolineato @JasonTuran. E questo sembra essere il più vicino possibile ad avere un nome distinto.
In genere, ho visto che il codice profondamente indentato è di solito un codice problematico. Se stai affrontando questo problema, fai un passo indietro e valuta se la tua funzione sta facendo troppe cose.
Allo stesso tempo, per rispondere alla tua domanda, se è necessario un rientro così profondo, ti suggerirei di lasciarlo lì. Per la semplice ragione che in tale codice, il rientro aiuterà poiché è probabilmente un pezzo di codice molto lungo.
Suddividere i componenti nidificati (specialmente quelli ripetuti) in funzioni separate (questo è più semplice se il linguaggio supporta le chiusure) o sostituire una serie di loop nidificati con una ricorsione.
Inoltre, rientra due spazi anziché quattro.
Non vedo le rientranze profonde come un problema categorico da rimuovere (né vedo il refactoring come la vera risposta a tutto).
In genere invece di if nidificati, mi piace scrivere istruzioni logiche:
if (foo && bar && baz)
piuttosto che
if foo
if bar
if baz
Non ci credevo, ma secondo Code Complete questo è un posto appropriato da usare break
(se la tua squadra è a bordo). Immagino che questo sia più accettabile con i programmatori C ++, dove break
usato nelle switch
istruzioni di quanto non lo sia con i programmatori Delphi dove break
viene usato solo quando non hai voglia di scrivere un while
ciclo.
La rientranza è davvero un pensiero per combattere, anzi. Quello che ho imparato a fare è prima dividere il metodo in pezzi, quindi usare uno strano trucco per saltare tutti i pezzi successivi se un pezzo falliva. Ecco un esempio:
Invece di :
{if (networkCardIsOn() == true)
{if (PingToServer() == true)
{if (AccesLogin(login,pass) == true)
{if (nextCondition == true)
...
}
}
}
Attualmente scrivo:
{vbContinue = true;
if (vbContinue) {
vbContinue = networkCardIsOn();
if (vbContinue == false) {
code to Handle This Error();
}
}
if (vbContinue) {
vbContinue = PingToServer();
if (vbContinue == false) {
code to HandleThisError2();
}
}
if (vbContinue) {
vbContinue = AccesLogin(login,pass);
if (vbContinue == false) {
HandleThisErrorToo();
}
}
...
All'inizio mi è sembrato strano, ma da quando lo uso, il costo di manutenzione è stato diviso per metà e il mio cervello è più freddo alla fine della giornata.
In effetti, il vantaggio introdotto da questa "tecnica" è che la complessità del codice è davvero divisa perché il codice è meno denso.
Durante la lettura del codice, non è necessario ricordare nulla delle condizioni passate: se ci si trova a quel punto X nel codice, i passaggi precedenti vengono passati e sono riusciti.
Un altro vantaggio è che il "percorso di fuga e condizione" da tutti quegli "if-else" nidificati è semplificato.