Recensioni di codice: fonti comuni di violazioni estreme e come evitare argomenti su come risolverli

I coltelli sono disegnati. Le lame sono affilate per il conflitto. Una controversia infuria tra gli sviluppatori. Le passioni dei programmatori sono infiammate non dal software difettoso ma dal codice egregiamente conciso o dettagliato. Quelle linee sono segni di un hack. Qualsiasi programmatore che non è d'accordo è un dilettante. Solo un neofita produrrebbe metodi e blocchi che violano così chiaramente il buon gusto. Tuttavia, preferenze diverse, non leggi della natura, sono la fonte di questo conflitto e del vetriolo. L'odio tra gli sviluppatori è, in questo caso, il risultato di diverse inclinazioni verso il commercio di sintonia per scopi diversi. Questi obiettivi, e la tendenza per essi, sono diversi per ogni sviluppatore, portando a conflitti costanti in determinate aree. Uno di questi posti è il codice wordy o pithy. Per ridurre al minimo il combattimento, una squadra può usare le revisioni del codice per evidenziare i segmenti più eclatanti, e il gruppo può discutere su quelle parti invece di discutere su ogni linea e blocco di una base di codice.

È possibile che determinati costrutti o tecniche di codice producano violazioni estreme e conducano argomentazioni sulla correzione di tali infrazioni. Risolvere questi abusi porta a discussioni intense. Questi disaccordi possono essere risolti, o almeno declassati, per le particolari caratteristiche e tecniche linguistiche elencate di seguito.

Operatore condizionale vs. istruzione If

Gli elementi linguistici, l'operatore condizionale e l'istruzione if, conducono ad argomenti, con diversi campi che sostengono che ognuno è la tecnica superiore per determinate operazioni. Queste azioni possono essere implementate in una miriade di modi, ogni tecnica porta vantaggi e svantaggi.

Dichiarazione If: la dichiarazione if può contribuire a un codice atrocemente voluminoso, quando la densità delle condizioni è elevata. Un'alta densità rende gonfio un blocco o un metodo. Tuttavia, anche il codice scritto con istruzioni if ​​è altamente debuggable, poiché uno sviluppatore può passare da una riga all'altra.

if (label1IsRequired) {
 label1.Color = "rosso";
} altro {
 label1.Color = "nero";
}
if (label2IsRequired) {
 label2.Color = “rosso”;
} altro {
 label2.Color = "nero";
}
if (label3IsRequired) {
 label3.Color = "rosso";
} altro {
 label3.Color = "nero";
}

Operatore condizionale: l'operatore condizionale può portare ad alcune linee flagrantemente terse, quando è usato come sostituto di diverse dichiarazioni if. Gli operatori condizionali incorporati rendono il codice, se portato all'estremo, molto difficile da leggere, testare o eseguire il debug. Qualsiasi blocco o metodo pesante per gli operatori condizionali è anche molto compatto, riducendo la quantità di codice che uno sviluppatore deve scansionare.

healthIndicatorColor = (health == “Buono”)? "Verde": (salute == "Fiera")? "Giallo": (salute == "scarso")? “Rosso”: (health == “life_support”)? “Arancione”: “viola”;

Risoluzione potenziale: gli operatori condizionali sono vantaggiosi quando sostituiscono un'alta densità di valori impostati in base alle condizioni implementate attraverso istruzioni if. Gli operatori condizionali sono distruttivi, quando sostituiscono anche un paio di decisioni incorporate l'una nell'altra. Gli imperativi che si adattano facilmente su una riga sono un obiettivo primario per gli operatori condizionali, mentre le condizioni che richiedono più righe sono il dominio delle istruzioni if. Qualsiasi uso eclatante di istruzioni if ​​o operatori condizionali dovrebbe essere corretto per distribuire l'uso appropriato di uno di questi costrutti. (Nota: la modifica potrebbe richiedere un refactoring significativo.)

if (salute == "Buono") {
 healthIndicatorColor = “green”;
} altrimenti if (health == “Fair”) {
 healthIndicatorColor = “giallo”;
} altrimenti if (health == “poor”) {
 healthIndicatorColor = “rosso”;
} else if (health == "life_support") {
 healthIndicatorColor = “orange”;
} altro {
 healthIndicatorColor = “purple”;
}
label1.Color = (label1IsRequired)? "rosso nero";
label2.Color = (label2IsRequired)? "rosso nero";
label3.Color = (label3IsRequired)? "rosso nero";

Dichiarazioni di reso multiple vs. una dichiarazione di reso

Due stili particolari che portano ad argomenti sono più ritorni e singoli ritorni. I disaccordi emergono sul fatto che i metodi debbano avere un'istruzione return o se siano accettabili più dichiarazioni return. Ogni approccio ha aspetti positivi e negativi.

Dichiarazioni di restituzione multiple: le dichiarazioni di restituzione multipla possono contribuire al codice che è difficile da capire, seguire e testare. Tuttavia, i metodi con più ritorni possono essere più brevi delle funzioni con un singolo ritorno.

SomeDataType someMethod (param1, param2, param3) {
 SomeDataType retVal;
 if (param1 == null) {
 retVal = null
 }
 if (retVal == null) {
 return retVal;
 }
 
 if (param2! = null) {
 retVal = param2;
 }
 if (retVal.Equals (param2)) {
 return retVal;
 }
 
 retVal = param3;
 return retVal;
}

Una dichiarazione di ritorno: una singola dichiarazione di ritorno può portare a metodi lunghi. Tuttavia, queste procedure hanno un unico punto di uscita, semplificando i test e il debug.

SomeDataType someMethod () {
 SomeDataType retVal;
 // codice di centinaia o migliaia di righe
 return retVal;
}

Risoluzione potenziale: i ritorni multipli rendono il codice difficile da capire, seguire e testare quando vengono utilizzati in modo incoerente. Le singole dichiarazioni di ritorno portano a metodi lunghi, quando sono seguite da lunghi tratti di codice. Tali intervalli possono essere abbreviati, o almeno resi leggibili, utilizzando diverse dichiarazioni di ritorno anziché una. I ritorni singoli sono perfettamente accettabili, quando seguono brevi tratti di codice. Qualsiasi uso improprio evidente di una singola dichiarazione di reso o di più resi deve essere corretto per applicare un caso d'uso accettato di uno di tali stili. (Nota: la correzione potrebbe richiedere un refactoring significativo.)

SomeDataType someMethod (param1, param2, param3) {
 if (param1 == null || param3 == null) {
 restituisce null;
 }
 
 SomeDataType retVal = null;
 if (param2! = null {
 retVal = param2;
 } else if (param1! = null) {
 retVal = param1;
 } ele if (param3! = null) {
 retVal = param3;
 }
 return retVal;
}
SomeDataType someMethod (param1, param2) {
 SomeDataType retVal = null;
 per (int i = 0; i  if (param1 [i] .equals (param2)) {
 retVal = param1 [i];
 rompere;
 }
 }
 return retVal;
} Interrompi e continua l'utilizzo nei loop

I costrutti break e continue sono oggetto di intensi dibattiti. Da un lato dell'argomento, gli sviluppatori sostengono che interrompere e continuare può semplificare il flusso di controllo. Altri programmatori sostengono che queste funzionalità complicano la logica di un programma. Break and continue può sicuramente essere usato per semplificare o complicare il codice. Queste linee possono essere individuate.

Interrompi e continua l'uso: gli elementi possono semplificare il codice, ma possono anche complicarli inutilmente.

SomeDataType someMethod (param1, param2) {
 SomeDataType retVal = null;
 per (int i = 0; i  if (param1 [i] == null) {
 Continua;
 }
 if (param1 [i] .equals (param2)) {
 retVal = param1 [i];
 rompere;
 }
 }
 return retVal;
}
SomeDataType someMethod (data, param1) {
 SomeDataType retVal = null;
 fare qualcosa:
 per (int i = 0; i  if (i> = data.length) {
 rompere;
 }
 if (data [i] .equals (param1)) {
 retVal = data [i];
 } altro {
 Continua;
 }
 }
if (retVal == null) {
 data - refreshData ();
 vai a dosomething;
 }
return retVal;
}

Risoluzione potenziale: la maggior parte degli sviluppatori sostiene che il codice dovrebbe utilizzare semplici meccanismi per il flusso di controllo. Quali meccanismi specifici sono semplici è la fonte del dibattito. Questa argomentazione diventa molto meno accesa, quando ogni strumento viene utilizzato in modi ampiamente accettati. Esistono approcci accettati per interrompere e continuare. Attenersi a tali convenzioni per prevenire disaccordi e semplificare il flusso di controllo. Qualsiasi mezzo di controllo che viola egregiamente tali standard dovrebbe essere corretto senza discussione.

SomeDataType someMethod (data, param1) {
 SomeDataType retVal = null;
 per (int i = 0; i  if (data [i] == null) {
 Continua; // salta il resto del loop
 }
 if (data [i] .equals (param2)) {
 retVal = data [i];
 rompere; // non fare più loop in b / c che ho finito
 }
 }
 return retVal;
}

Eccezioni difensive

Le eccezioni sono un mezzo per indicare un problema o per evitare un problema futuro. Quali mal di testa dovrebbero essere indicati o scardinati da quali parti del codice sono un argomento di acceso dibattito. A un'estremità del disaccordo, i programmatori sostengono che le diffuse eccezioni difensive prevengono gli errori e li rendono facili da localizzare. Tuttavia, tale elenco di difesa può rendere il codice gonfio e difficile da comprendere, come hanno sostenuto alcuni programmatori. Gli sviluppatori di entrambe le parti del dibattito hanno ragione. Le eccezioni difensive hanno sia vantaggi che svantaggi.

Vantaggi e detriti delle eccezioni difensive: la protezione da errori e altri problemi può essere salvaguardata, con minimi inconvenienti, utilizzando eccezioni difensive. Quei difetti si ingigantiscono, quando la tecnica è indiscriminatamente.

void someMethod (param1, param2) {
 if (param1 == null || param2 == null) {
 lancia nuovo ArgumentNullException ("Manca uno o più parametri");
 }
 // fa cose di metodo
}
void someMethod (param1, param2) {
 // dozzine di linee di controlli difensivi… ..
 // fa il resto del metodo
}

Risoluzione potenziale: le carenze delle eccezioni difensive sono minime, quando vengono impiegate in usi accettati. Qualsiasi dispiegamento della tecnica che si discosta da tali convenzioni dovrebbe essere corretto, a meno che non venga fornito un motivo convincente.

public void someMethod (param1, param2) {
 // controlla ogni parametro in un metodo pubblico
 if (param1 == null || param2 == null) {
 lancia nuovo ArgumentNullException ("Manca uno o più parametri");
 }
 
 // elimina i problemi causati da dati non validi
 if (! isValid (param1) ||! isValid (param2)) {
 lancio nuovo InvalidParameterException ("Uno o più parametri non sono validi");
 }
 
 // fa qualcosa con i parametri
}

Incartare

Questi costrutti e tecniche di codice sono utilizzati da sviluppatori buoni e cattivi. I programmatori sono persone. Gli esseri umani hanno tendenze. Queste inclinazioni si manifestano nel codice. Occasionalmente, gli impulsi di uno sviluppatore lo portano a scrivere codice che altri programmatori criticano giustamente. Lo sviluppatore messo alla gogna non è necessariamente un cattivo programmatore. Il programmatore che lo critica non è necessariamente un buon sviluppatore. Entrambe le persone sono state probabilmente portate fuori strada, a un certo punto, dalle loro preferenze. Questi desideri non dovrebbero indurre lo sviluppo a degenerare in un flusso infinito di insulti lanciati l'uno contro l'altro. Piuttosto, i programmatori dovrebbero rivedere il codice reciproco, limitare le loro battaglie alle sezioni peggiori e accettare di risolvere alcuni argomenti attraverso le regole sopra esposte.