bool fn()
{
if(something bad happen)
return false;
..
}
void gn()
{
assert(something == true);
..
}
Quando si scrive una funzione nel codice di produzione, in quale modo devo scegliere ?Assert vs. return false?
bool fn()
{
if(something bad happen)
return false;
..
}
void gn()
{
assert(something == true);
..
}
Quando si scrive una funzione nel codice di produzione, in quale modo devo scegliere ?Assert vs. return false?
Assert
, almeno in .NET, viene utilizzato per il test principalmente. Il suo uso specifico viene messo succintamente here:
Un'asserzione è meglio utilizzato per testare una condizione solo quando tutte le seguenti attesa:
* the condition should never be false if the code is correct,
* the condition is not so trivial so as to obviously be always true, and
* the condition is in some sense internal to a body of software.
Nel codice di produzione, mi consiglia il primo metodo; oppure a try/catch
se non si prevede che "qualcosa di brutto" possa accadere; se è una condizione eccezionale.
Se si dispone di un invariante che dovrebbe sempre o non essere mai lì (indipendentemente dallo stato del sistema), allora questo è un buon candidato per un Assert
al di fuori di un solo caso di test.
Ho visto Asserzioni nel codice di produzione; normalmente in design-by-contract style code. Li vedo molto più spesso nei test unitari.
IMHO, assert
è quello di affermare lo sviluppatore in ambiente di debug che qualcosa di inaspettato (sbagliato) sta accadendo. Non tutte le asserzioni devono essere critiche e spetta agli sviluppatori mettere le asserzioni per i veri fallimenti.
Nella modalità di rilascio è necessario restituire false solo quando l'errore è critico e non è necessario continuare l'esecuzione.
Utilizzare l'avviso quando qualcosa di brutto non dovrebbe mai accadere se il codice funziona correttamente. Un esempio:
int div(int a, int b) {
assert(b != 0);
return a/b;
}
Qui, il codice chiamante ha la responsabilità di fare in modo che div() non viene mai chiamato con un secondo parametro zero. Se lo fa, il tuo codice è in uno stato sconosciuto e dovrebbe essere terminato.
Utilizzare if() ... restituire quando si può verificare un errore che può essere risolto. Ad esempio, l'apertura di un file può fallire e dovrebbe essere rilevata e gestita dal programma - l'errore non pone il programma in uno stato sconosciuto.
La risposta semplice è entrambe, ma la seconda è più importante. L'assert controlla un'ipotesi di progetto, quindi prima di chiamare fn() avrei pre-condizioni, ad es. che una determinata risorsa potrebbe essere disponibile. Generalmente, l'affermazione non fa nulla nel codice di rilascio, anche se non necessariamente. Se è importante che la tua funzione, gn, richieda che una cosa sia vera per funzionare correttamente, essa usa l'istruzione if e restituisce e codice di errore, oppure genera un'eccezione. Assert generalmente non fa nulla nel codice di rilascio e quindi la tua funzione potrebbe bloccarsi in questa situazione.
Vedere anche le risposte più dettagliate date a this question
Che in realtà dipende dall'ambiente lingua/runtime, e la natura dell'errore.
In generale, il programma non può recuperare da errori di asserzione (cioè si blocca). Potrebbe essere o non essere quello che vuoi.
Se il proprio ambiente supporta le eccezioni, è possibile utilizzare anche queste, che consentono di gestire l'errore se lo si desidera e di interrompere un messaggio utile se non si gestisce l'errore.
La parola "return false" è soggetta a errori (b/c le persone dimenticano di controllare il valore restituito), quindi lo userei solo se né le affermazioni né le eccezioni sono disponibili.
prendere in considerazione anche di utilizzare un'eccezione quando "qualcosa di brutto è accaduto" = qualcosa di eccezionale ...
dipende da ciò che si sta controllando. Nel primo caso se la cosa brutta che si è verificata interrompe il contratto per la tua classe, genera un'eccezione che significherà qualcosa per il cliente. Restituire false non permetterà a nessuno di sapere che lo stato non è corretto.
Usa asserire su entrambi i documenti e confermare che invarianti programma (sai che sarà sempre true) in attesa. In qualcosa di più di un semplice programma, la tua comprensione del problema si svilupperà man mano che scrivi il codice, quindi il codice più vecchio potrebbe avere un modello diverso, e gli asserti aiuteranno a risalire.
Per qualcosa che può cambiare (in particolare al di fuori del controllo programmi) è necessario essere in grado di segnalare errori (ad esempio valori dei parametri invaid passati a una funzione di libreria, il file non esiste), utilizzare il meccanismo preferito di piattaforma/lingue (eccezioni , valori di ritorno, ...).
Le asserzioni sono solo a scopo di controllo della qualità. La loro rimozione non deve alterare il comportamento del tuo programma (ad esempio, il tuo programma non deve basarsi su asserzioni per gestire qualsiasi cosa). Usa le eccezioni se succede qualcosa che non dovrebbe accadere, ma può accadere in circostanze sfavorevoli - ad es. connessione di rete persa. Utilizza i valori di ritorno per indicare il successo quando l'errore è anche un'opzione.
Vorrei dire affermare quando qualcosa come un contratto è rotto. Cosa intendo con questo: in matematica, la divisione per zero non è definita. Quindi il contratto con la funzione di divisione è rotto. Più sul contratto vedi il linguaggio di progromming Eiffel.
In alcuni casi implementerei una seconda funzione che ha parametri aggiuntivi con valori predefiniti e gestisce l'eccezione, come StrinToIntegerDef (stringa, impostazione predefinita), che restituisce il valore predefinito quando stringa non converte in numero intero.
Il ritorno falso può essere eseguito nei casi in cui la normale esecuzione del programma non è in gioco.
Preferirei eccezioni significative, almeno della forma assert (condizione, messaggio), se la lingua lo consente.
Inoltre, se esistono diverse possibilità di errore, restituire alcuni valori enum anziché false.
Utilizzare le asserzioni per imporre la funzione preconditions (condizioni che devono essere valide se la funzione deve avere alcun significato).
Anche invarianti e (alla fine delle funzioni) postcondizioni. – Richard
Il tuo "return false" è chiamato anche un GuardClause - come spiegato da Ward Cunningham:
... [G] uards sono simili alle affermazioni a che sia proteggere il codice successiva da casi speciali.Le protezioni differiscono da dalle affermazioni in quanto apportano un contributo tangibile alla logica del metodo e pertanto non possono essere eliminate in modo sicuro come parte di un'ottimizzazione. I ha preso in prestito il termine guardia da EwDijkstra quando si nomina questo modello.
Se la clausola di guardia è di qualsiasi complessità, è spesso utile incapsulare utilizzando BouncerPattern.
Come osserva Ward, è necessario utilizzare le asserzioni quando il codice può essere omesso in modo sicuro. Se nella tua lingua è presente una parola chiave assert
, i compilatori spesso rimuovono le asserzioni dal codice di produzione.
Oggi riconosciamo che nella maggior parte dei casi non è una buona idea inserire affermazioni nel codice. Classica separazione delle preoccupazioni. Le affermazioni del codice separate e incapsulano in in un test di unità. Ciò elimina la complessità di dover ricompilare il codice senza asserzioni da distribuire (non distribuirai i tuoi test ...) e ti offre anche una serie di test che possono essere eseguiti continuamente per catturare le regressioni e guidare i refactoring.
altre risposte hanno detto Design by Contract quali fattori completamente queste preoccupazioni fuori del codice e nella dichiarati precondizioni, postcondizioni e invarianti che vengono applicate in tutto il codice in questione. Se lo fai spesso, puoi prendere in considerazione la possibilità di esaminare una struttura Design by Contract per la tua lingua.
Sì, è il mio punto di vista. Sto usando le chiamate assert() per verificare invarianti nel mio programma. Quando tali invarianti non vengono rispettati, poiché la chiamata assert genera un coredump per la mia app (durante il debugging), sono sicuro che non salterò tale problema. Inoltre, mi aspetto che i prossimi "sviluppatori incaricati" notino qualcosa che non va quando l'applicazione si arresta in modo anomalo ... Mentre una semplice dichiarazione di ritorno potrebbe alla fine portare a scaricare alcune righe in un file, che lo sviluppatore potrebbe evitare. Non voglio dire che potresti evitare se() prova. Voglio solo dire che assert() è un modo per garantire che gli sviluppatori siano stati avvertiti di un problema. –