restituendo null vs return zero, quale sarebbe meglio?

3

Ho ereditato un progetto che sto gestendo e che devo mantenere in sospeso la riqualificazione della base di codice. Al momento mi viene assegnato il compito di aggiungere piccole funzionalità dappertutto e ho preso l'abitudine di restituire null anziché zero in parti del codice su cui sto lavorando.

Il problema è che un client che utilizza questo codice e parti di codice che richiedono dati dalle funzioni implementate ricevono null e riversano la traccia dello stack nell'interfaccia utente.

Vorrei evitare tutto questo dal mio input ma senza NullPointerException c'è il potenziale che gli errori vengano introdotti nei dati del client che potrebbero non essere notati.

Di solito avrei creato il mio sistema di notifica degli errori, ma non ho mai ereditato un progetto prima. Quindi non sono sicuro se continuare su questa strada. Continuo a credere che il dump dello stack sia preferibile alla corruzione / imprecisione dei dati non segnalata.

    
posta Dark Star1 31.05.2012 - 11:37
fonte

5 risposte

3

Attualmente sto leggendo il codice pulito di Robert C. Martin e ho terminato il capitolo sulla gestione delle eccezioni di alcuni giorni fa. Quello che dice è:

Returning null from methods is bad, but passing null into methods is worse

Continua a elencare esempi concreti su come evitarlo, principalmente:

  • quando si restituiscono elenchi o matrici, restituire l'elenco o gli array vuoti
  • quando si restituiscono oggetti non di raccolta, si preferisce restituire oggetti "caso speciale", OPPURE generare eccezioni. Per "caso speciale", suppongo che stia per esempio restituendo una stringa vuota ( StringUtils.EMPTY se si lavora con il pacchetto Apache Commons), o un "NullObject" (grazie a Thorbjørn non sapevo che fosse un pattern in effetti). ..

Se costruisci la tua applicazione in questo modo, avrai meno "assegni nulli" da scrivere, qualcosa che lui dice dicendo che il più delle volte, avere metodi con molti "assegni nulli" è un brutto segno.

NullPointerException dovrebbe essere previsto durante lo sviluppo, quando si lavora con un'API che potrebbe non essere chiara con il modo in cui tratta i suoi argomenti, e penso che dovrebbero essere considerati bug nel codice di produzione. Intendo dire che se lavoro con una API che conosco e capisco, non mi aspetto di prendere NullPointerException , e mi aspetto di vederli nei file di registro se faccio qualcosa di sbagliato (cosa che purtroppo accade ancora dopo la programmazione per alcuni anni: -).

Naturalmente, come sempre con le "migliori pratiche", non devi seguire ciecamente questi principi. Queste sono le "migliori pratiche", non "queste sono le sole buone pratiche": -)

Non ho letto il programmatore pragmatico di Andrew Hunt e David Thomas ... ancora, ma questo link al blog potrebbe essere interessante. Spiega in poche parole le leggere differenze nella gestione delle eccezioni tra i due libri, se sei curioso.

    
risposta data 31.05.2012 - 12:50
fonte
6

È una questione di preferenze personali e quanto sia importante avere tutti i dati corretti.

Restituire un numero ovviamente errato ma ancora valido è meno probabile che causi eccezioni impreviste nel client, ma corre il rischio che l'errore non venga notato. (spesso -1 è usato piuttosto che zero come più chiaramente errato piuttosto che un risultato reale)

Il ritorno di Null o un'eccezione farà cadere l'applicazione client se non gestita correttamente, ma garantisce che l'errore venga notato o almeno gestito in qualche modo.

Entrambi gli approcci sono accettabili, tuttavia è meglio essere coerenti.

    
risposta data 31.05.2012 - 12:23
fonte
2

Esistono molti casi in cui Null è un risultato valido e previsto per una funzione e molti casi in cui non è un valore di ritorno valido.

Per le funzioni che restituiscono un riferimento a dati "opzionali", allora un valore di ritorno Nullo può essere valido. Esempi per questo sarebbero la lettura della colonna di una tabella di database in cui è consentito Null. Un altro esempio è un riferimento a un oggetto dati che è temporaneo o definito in un secondo momento.

In questi casi, è errore del programmatore non controllare che il valore sia impostato prima di utilizzare il riferimento.

Per le funzioni che dovrebbero creare una nuova istanza di un oggetto, restituire un riferimento a un oggetto deve avere o una funzione che restituisce un oggetto che contiene il contesto del risultato, quindi tutti quei casi dovrebbero sempre restituire un riferimento valido. Se non è possibile farlo, deve essere generata un'eccezione ben documentata.

Ci sono molti schemi di progettazione là fuori per gestire le cose in modi diversi per rendere più facile il test o la stabilità.

Ad esempio, piuttosto che avere una funzione crea un nuovo oggetto per contenere un risultato e restituirlo. La funzione dovrebbe prendere un oggetto come argomento e modificarlo. Piuttosto quindi avere un costruttore che crea oggetti (che potrebbero fallire), il costruttore non dovrebbe fare nulla e avere una funzione di inizializzazione con tutti gli oggetti richiesti come parametri.

    
risposta data 31.05.2012 - 15:46
fonte
0

La risposta qui sta nel codice cliente. Se è possibile coordinare il test delle modifiche con i proprietari del codice client e far sì che possano risolvere eventuali problemi esposti o sviluppare funzionalità per gestire le modifiche introdotte, questa sarebbe una soluzione adeguata. Se tuttavia è difficile o impossibile coordinare i test, è necessario limitare o evitare l'uso inaspettato delle proprie API.

Data una maggiore libertà di migliorare la comunicazione dei confini, dovresti cercare di evitare null come valore eccezionale, convenzioni come -1 come usate, ad esempio, i flussi sono più sicuri ma non ideali ed a volte è difficile scegliere un valore specifico di un tipo per rappresentare un caso eccezionale in quanto potrebbero essere introdotte ambiguità o nessun valore particolare disponibile poiché tutti i valori del tipo sono potenzialmente legittimi.

Linguaggi di alto livello come Haskell e Scala promuovono l'uso di tipi di opzioni per racchiudere un valore di un particolare tipo o un valore di un tipo diverso che rappresenta un valore o uno stato eccezionale. L'utilizzo di tipi di opzioni come parametri obbliga il client a gestire o propagare il contenuto dell'opzione anziché affidarsi a convenzioni su valori specifici o fidarsi del fatto che non verrà ricevuto nulla. I tipi di opzioni aprono un'intera gamma di tecniche e modelli utili.

    
risposta data 31.05.2012 - 13:07
fonte
-1

Prima di tutto, come detto, il linguaggio usato porta a differenze nella risposta. Ma il problema principale è la cosa:

  • Il ritorno di un valore " 0 " probabilmente porterà a errori silenziosi. Se questo è uno speciale NullObject o 0 o "Jan 1, 1900" . Per lo meno, ciò aggraverà i problemi di dati sporchi
  • Stai lavorando su un prodotto esistente e i consumatori esistenti dovrebbero rimanere funzionali. Non solo, ma il resto della codebase non dovrebbe regredire neanche.

Quindi, come gestirlo?

  • Utilizza un preprocessore (sì, puoi farlo anche quando la lingua non è definita per usarne uno), e crea un token NULL che è sostituito da null o 0 in base al fatto che tu sia in modalità "debug".
  • Per i client esterni, isolali dalla versione "corretta", facendo in modo che l'interfaccia utilizzata diventi un'interfaccia di compatibilità che (preferibilmente generata automaticamente) traduca tra la vecchia e la nuova (migliore)

Ciò consente di mantenere le cose funzionanti come sono per "produzione", ma comunque di "eseguire il debug" o "testare" il sistema con maggiore correttezza. La modalità debug non userebbe anche il livello di compatibilità in modo tale che la correttezza del client possa essere (indipendentemente) testata.

    
risposta data 31.05.2012 - 16:02
fonte

Leggi altre domande sui tag