Cosa definisce il codice robusto?


42

Il mio professore continua a fare riferimento a questo esempio Java quando parla di codice "robusto":

if (var == true) {
    ...
} else if (var == false) {
    ...
} else {
    ...
}

Afferma che "codice robusto" significa che il tuo programma tiene conto di tutte le possibilità e che non esiste un errore: tutte le situazioni sono gestite dal codice e risultano in uno stato valido, quindi "altro".

Sono dubbioso, tuttavia. Se la variabile è un valore booleano, che senso ha verificare un terzo stato quando un terzo stato è logicamente impossibile?

"Non avere nulla di simile ad un errore" sembra anche ridicolo; anche le applicazioni di Google mostrano gli errori direttamente all'utente invece di ingoiarli silenziosamente o in qualche modo considerandoli come stato valido. Ed è buono - mi piace sapere quando qualcosa va storto. E sembra abbastanza affermare che un'applicazione non avrebbe mai avuto errori.

Allora, qual è la definizione effettiva di "codice robusto"?



4
Questo sarebbe valido solo in un linguaggio non fortemente tipizzato. In un linguaggio fortemente tipizzato una variabile di tipo booleano (non un intero che si propone come un booleano), può essere solo vera o falsa, non esiste una terza opzione ...
Marjan Venema,

23
chiedigli come testeresti la copertura sul terzo caso, perché il codice robusto dovrebbe sicuramente richiedere un test e se non riesci a testare il terzo caso, non saresti in grado di trovare eventuali bug che potrebbero nascondersi lì.
gbjbaanb,

2
@Marjan - in una lingua non tipizzata fortemente si potrebbe semplicemente scrivere: if (var) {} else {}
kevin cline

2
Non conosco alcuna lingua in cui sia x che! X potrebbero essere vere. Nota che non ho suggerito "if (x == true) ..."; Adoro simili confronti.
Kevin Cline,

Risposte:


33

che senso ha controllare un terzo stato quando un terzo stato è logicamente impossibile?

Che dire di un Boolean?che consente uno NULLstato che non è né vero né falso. Ora cosa dovrebbe fare il software? Alcuni software devono essere altamente resistenti agli urti come i pacemaker. Hai mai visto qualcuno aggiungere una colonna a un database che era un Booleane inizializzare NULLinizialmente i dati correnti ? So di averlo visto.

Ecco alcuni link che discutono su cosa significhi essere robusti in termini di software:

Se pensi che ci sia una definizione universalmente concordata di "robusto" qui, buona fortuna. Ci possono essere alcuni sinonimi come a prova di bomba o a prova di idiota. Il programmatore del nastro adesivo sarebbe un esempio di qualcuno che di solito scrive codice robusto almeno nella mia comprensione dei termini.


13
Se questo fosse un valore booleano nullable, sia Java che c # verrebbero lanciati, pertanto è necessario verificare prima null.
Esben Skov Pedersen,

Non sembra esserci una definizione universalmente concordata di cosa siano un gatto o un cane.
Tulains Córdova,

11

Per motivi di discussione, un Bool può avere 2 stati, Vero o Falso. Qualsiasi altra cosa non è conforme alla specifica delle lingue di programmazione. Se la catena degli strumenti non è conforme alle sue specifiche, si viene sottoposti a qualsiasi tipo di operazione. Se uno sviluppatore ha creato un tipo di Bool con più di 2 stati, è l'ultima cosa che farebbe mai sulla mia base di codice.

Opzione A.

if (var == true) {
    ...
} else if (var == false) {
    ...
} else {
    ...
}

Opzione B

if (var == true) {
    ...
} else {
    ...
}

Affermo che l'opzione B è più robusta .....

Qualsiasi twit può dirti di gestire errori imprevisti. Di solito sono banalmente facili da rilevare quando ci pensi. L'esempio che il tuo professore ha dato non è qualcosa che potrebbe accadere, quindi è un esempio molto scarso.

A è impossibile testare senza cablaggi di prova contorti. Se non riesci a crearlo, come lo testerai? Se non hai testato il codice, come fai a sapere che funziona? Se non sai che funziona, non stai scrivendo un software affidabile. Penso che lo chiamino ancora Catch22 (film fantastico, guardalo qualche volta).

L'opzione B è banale da testare.

Prossimo problema, fai al professore questa domanda "Cosa vuoi che faccia se un booleano non è né vero né falso?" Ciò dovrebbe condurre a una discussione molto interessante .....

Nella maggior parte dei casi, una discarica di base è adeguata, nel peggiore dei casi disturba l'utente o costa molti soldi. E se, diciamo, il modulo fosse il sistema di calcolo del rientro in tempo reale dello Space Shuttle? Qualsiasi risposta, per quanto imprecisa, non può essere peggio che interrompere, il che ucciderà gli utenti. Quindi cosa fare, se sai che la risposta potrebbe essere sbagliata, scegli il 50/50 o interrompi e vai al fallimento del 100%. Se fossi un membro dell'equipaggio, prenderei il 50/50.

L'opzione A mi uccide L'opzione B mi dà persino la possibilità di sopravvivere.

Ma aspetta - è una simulazione del rientro dello space shuttle - e allora? Interrompi così lo sai. Sembra una buona idea? - NON - perché devi testare con il codice che intendi spedire.

L'opzione A è migliore per la simulazione, ma non può essere distribuita. È inutile che l'opzione B sia il codice distribuito, quindi la simulazione esegue lo stesso dei sistemi live.

Diciamo che questa era una preoccupazione valida. La soluzione migliore sarebbe quella di isolare la gestione degli errori dalla logica dell'applicazione.

if (var != true || var != false) {
    errorReport("Hell just froze over, var must be true or false")
}
......
if (var == true){
 .... 
} else {
 .... 
}

Altre letture : macchina a raggi X Therac-25, guasto del missile Ariane 5 e altri (il collegamento ha molti collegamenti interrotti ma informazioni sufficienti che Google aiuterà)


1
"... errori imprevisti. Di solito sono banalmente facili da rilevare quando ci pensi" - ma quando ci pensi, non sono più inaspettati.
gbjbaanb,

7
C'è qualche domanda sul fatto che il tuo codice if (var != true || var != false) {debba essere un &&invece.

1
Posso facilmente pensare a un bool che non è né vero né falso, ma è ancora inaspettato. Nel caso in cui dici che un bool non può essere nient'altro, se controllo se una lettera è un carattere cifra e poi lo converto nel suo valore intero, posso facilmente pensare che quel valore intero sia inferiore a 0 o maggiore di 9, ma è comunque inaspettato.
gnasher729,

1
I booleani null sono supportati in Java e C # e dispongono di un'applicazione reale. Prendi in considerazione un database contenente un elenco di persone. Dopo un po 'decidi di aver bisogno di un campo di genere (isMale). Null significa "mai chiesto quindi non lo so"; vero significa maschio e falso significa femmina. (OK, trans-gender omesso per semplicità ...).
kiwiron,

@kiwiron: sarebbe una soluzione migliore non usare un tipo di enumerazione, "Maschio", "Femmina", "Non chiedere". Le enumerazioni sono migliori - possono essere estese quando si presenta la necessità (ad esempio nella tua mente asessuata, ermafrodita, "Rifiutato di rispondere").
mattnz,

9

In realtà il tuo codice non è più robusto ma MENO robusto. Il finale elseè semplicemente un codice morto che non puoi testare.

In software critici come veicoli spaziali, codice morto e codice generalmente non testato è vietato: se un raggio cosmico produce un singolo evento sconvolto che a sua volta attiva il tuo codice morto, tutto è possibile. Se la SEU attiva una parte di codice robusto, il comportamento (imprevisto) rimane sotto controllo.


Non capisco nei veicoli spaziali è vietato il codice morto? cioè non puoi scrivere l'ultimo? dal momento che non puoi provarlo non puoi inserirlo? ma poi cosa significa "Se la SEU attiva una porzione di codice robusto, il comportamento (imprevisto) rimane sotto controllo."
squallido

5
Sì, nello spazio critico il test del software deve essere coperto al 100% e di conseguenza è vietato il codice irraggiungibile (noto anche come codice morto).
mouviciel,

7

Penso che il professore possa confondere "errore" e "errore". Il codice robusto dovrebbe certamente avere pochi / nessun bug. Il codice robusto può, e in un ambiente ostile, avere una buona gestione degli errori (che si tratti di una gestione delle eccezioni o di test rigorosi sullo stato del reso).

Sono d'accordo che l'esempio di codice del professore sia sciocco, ma non sciocco come il mio.

// Assign 3 to x
var x = 3;
x = 3;   // again, just for sure
while (x < 3 or x > 3) { x = 3; }  // being robust
if (x != 3) { ... }  // this got to be an error!

1
L'ultimo, se certamente attivato, non richiederebbe davvero tanto sforzo. Qualsiasi programmatore C esperto ha visto i valori cambiare improvvisamente. Ovviamente logicamente, in un ambiente a thread singolo controllato, ciò non dovrebbe mai accadere. Nella vita reale, il codice all'interno dell'if alla fine accadrà. Se non c'è nulla di utile che puoi fare all'interno di questo se, allora non codificarlo! (Ho avuto un'esperienza divertente durante un particolare sviluppo di software in cui ho sollevato un'eccezione con parolacce nel caso in cui fosse successo qualcosa di impossibile ... indovina cosa è successo?).
Alex

2
Storia vera:boolean x = something(); if (x) { x = True // make sure it's really true, ... }
Andres F.

6

Non esiste una definizione concordata di Codice robusto , poiché per molte cose in programmazione è più o meno soggettivo ...

L'esempio fornito dal tuo professore dipende dalla lingua:

  • In Haskell, a Booleanpuò essere uno Trueo False, non esiste una terza opzione
  • In C ++, a boolpuò essere true, falseo (purtroppo) venire da un cast discutibile che lo ha messo in un caso sconosciuto ... Questo non dovrebbe accadere, ma potrebbe, a causa di un errore precedente.

Tuttavia, ciò che il tuo professore sta suggerendo oscura il codice introducendo una logica estranea per eventi che non dovrebbero accadere nel mezzo del programma di base, quindi ti indicherò, invece, verso la programmazione difensiva .

Nel caso universitario, potresti persino aumentarlo adottando una strategia di Design by Contract:

  • Stabilire invarianti per le classi (ad esempio, sizeè il numero di voci datanell'elenco)
  • Stabilire pre-condizioni e post-condizioni per ciascuna funzione (ad esempio, questa funzione può essere invocata solo con aun valore inferiore a 10)
  • Metti alla prova ciascuno di quelli nei punti di entrata e uscita di ciascuna delle tue funzioni

Esempio:

class List:
  def __init__(self, items):
    self.__size = len(items)
    self.__data = items

  def __invariant(self):
    assert self.__size == len(self.__data)

  def size(self):
    self.__invariant()

    return self.__size

  def at(self, index):
    """index should be in [0,size)"""
    self.__invariant()
    assert index >= 0 and index < self.__size

    return self.__data[index]

  def pushback(self, item):
    """the subsequent list is one item longer
       the item can be retrieved by self.at(self.size()-1)"""
    self.__invariant()

    self.__data.append(item)
    self.__size += 1

    self.__invariant()
    assert self.at(self.size()-1) == item

Ma il professore ha detto specificamente che era Java, e in particolare NON ha detto quale sia il tipo di var. Se è booleano, può essere vero, falso o nullo. Se qualcos'altro, può essere diverso da vero e diverso da falso. Sì, si sovrappongono tra robusto, difensivo e paranoico.
Andy Canfield,

2
In C, C ++ e Objective-C, un bool può avere un valore indeterminato, come qualsiasi altro tipo, ma qualsiasi assegnazione lo imposterà su vero o falso e nient'altro. Ad esempio: bool b = 0; b ++; b ++; imposterà b su vero.
gnasher729,

2

L'approccio del tuo professore è totalmente fuorviato.

Una funzione, o solo un po 'di codice, dovrebbe avere una specifica che dice cosa fa, che dovrebbe coprire ogni possibile input. E il codice dovrebbe essere scritto in modo tale che il suo comportamento sia garantito per corrispondere alle specifiche. Nell'esempio, scriverei le specifiche piuttosto semplici in questo modo:

Spec: If var is false then the function does "this", otherwise it does "that". 

Quindi scrivi la funzione:

if (var == false) dothis; else dothat; 

e il codice soddisfa le specifiche. Quindi il tuo professore dice: E se var == 42? Guarda le specifiche: dice che la funzione dovrebbe fare "quello". Guarda il codice: la funzione fa "quello". La funzione soddisfa le specifiche.

Laddove il codice del tuo professore rende le cose totalmente inattendibili è il fatto che con il suo approccio, quando var non è né vero né falso, eseguirà un codice che non è mai stato chiamato prima e che è completamente non testato, con risultati assolutamente imprevedibili.


1

Sono d'accordo con l'affermazione di @ gnasher729: l'approccio del tuo professore è totalmente fuorviato.

Robusto significa che è resistente a rotture / guasti perché fa pochi presupposti ed è disaccoppiato: è autonomo, auto-definito e portatile. Include anche l'essere adattabile alle mutevoli esigenze. In una parola, il tuo codice è duraturo .

Ciò si traduce generalmente in brevi funzioni che ottengono i loro dati dai parametri passati dal chiamante e l'uso di interfacce pubbliche per i consumatori (metodi astratti, wrapper, indiretta, interfacce in stile COM, ecc.) Piuttosto che funzioni che contengono un codice di implementazione concreto.


0

Il codice robusto è semplicemente un codice che gestisce bene i guasti. Ne più ne meno.

Di errori, ce ne sono di molti tipi: codice errato, codice incompleto, valori imprevisti, stati imprevisti, eccezioni, esaurimento delle risorse, .... Il codice robusto li gestisce bene.


0

Considererei il codice che hai fornito come esempio di programmazione difensiva (almeno quando uso il termine). Parte della programmazione difensiva è fare scelte che minimizzino le ipotesi fatte sul comportamento del resto del sistema. Ad esempio, quale di questi è meglio:

for (int i = 0; i != sequence.length(); ++i) {
    // do something with sequence[i]
}

O:

for (int i = 0; i < sequence.length(); ++i) {
    // do something with sequence[i]
}

(Nel caso in cui riscontri problemi nel vedere la differenza, controlla il loop test: il primo usa !=, il secondo usa <).

Ora, nella maggior parte dei casi, i due loop si comporteranno esattamente allo stesso modo. Tuttavia, il primo (confrontandolo con !=) fa un'ipotesi che iverrà incrementata una sola volta per iterazione. Se salta il valore, sequence.length()il ciclo potrebbe continuare oltre i limiti della sequenza e causare un errore.

Puoi quindi argomentare che la seconda implementazione è più solida: non dipende da ipotesi sul fatto che il corpo del loop cambi i(nota: in realtà fa ancora l'assunto che inon è mai negativo).

Per dare una motivazione al perché potresti non voler fare quell'ipotesi, immagina che il ciclo stia eseguendo la scansione di una stringa, eseguendo un po 'di elaborazione del testo. Scrivi il ciclo e tutto va bene. Ora i tuoi requisiti cambiano e decidi che devi supportare i caratteri di escape nella stringa di testo, quindi cambi il corpo del loop in modo tale che se rileva un carattere di escape (diciamo, barra rovesciata), si incrementa iper saltare il carattere immediatamente dopo l'escape. Ora il primo ciclo ha un bug perché se l'ultimo carattere del testo è una barra rovesciata, il corpo del ciclo aumenterà ie il ciclo continuerà oltre la fine della sequenza.


-1

Personalmente descrivo un codice come 'robusto' che ha questo, attributi importanti:

  1. Se mia madre si siede davanti e ci lavora, non può rompere il sistema

Ora, per pausa intendo o portare il sistema in uno stato instabile o causare un'eccezione UNHANDLED . Sai, a volte per un concetto semplice, puoi fare una definizione e una spiegazione complesse. Preferirei definizioni semplici. Gli utenti sono abbastanza bravi a trovare applicazioni robuste. Se l'utente dell'applicazione ti invia molte richieste di bug, perdita di stato, flussi di lavoro non intuitivi, ecc., Allora c'è qualcosa che non va nella tua programmazione.

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.