Programmazione difensiva: linee guida in Java
-
22-07-2019 - |
Domanda
I & # 8217; m da uno sfondo .NET e ora dilettandomi in Java.
Attualmente sto riscontrando grossi problemi nel progettare un'API in modo difensivo contro input errati. Supponiamo che io abbia il seguente codice (abbastanza vicino):
public void setTokens(Node node, int newTokens) {
tokens.put(node, newTokens);
}
Tuttavia, questo codice può fallire per due motivi:
- L'utente passa un nodo
null
. - L'utente passa un nodo non valido, ovvero uno non contenuto nel grafico.
In .NET, lancerei un ArgumentNullException
(piuttosto che un NullReferenceException
!) O un ArgumentException
rispettivamente, passando il nome dell'argomento offensivo ( nodo
) come
Java non sembra avere eccezioni equivalenti. Mi rendo conto che potrei essere più specifico e semplicemente lanciare qualsiasi eccezione si avvicini di più alla descrizione della situazione, o anche a scrivere la mia classe di eccezioni per la situazione specifica.
È questa la migliore pratica? Oppure ci sono classi di uso generale simili a ArgumentException
in .NET?
In questo caso ha anche senso verificare con null
? Il codice fallirà comunque e la traccia dello stack dell'eccezione conterrà la chiamata del metodo sopra. Il controllo su null
sembra ridondante ed eccessivo. Certo, la traccia dello stack sarà leggermente più pulita (poiché il suo obiettivo è il metodo sopra, piuttosto che un controllo interno nell'implementazione HashMap
di JRE). Ma questo deve essere compensato dal costo di un'ulteriore istruzione if
, che, inoltre, non dovrebbe mai avvenire comunque & # 8211; & nbsp; dopo tutto, passando null
al metodo precedente non è una situazione prevista, è un bug piuttosto stupido. Aspettarsi che sia decisamente paranoico & # 8211; & nbsp; e fallirà con la stessa eccezione anche se non lo cerco.
[Come è stato sottolineato nei commenti, HashMap.put
in realtà consente valori null
per la chiave. Quindi un controllo contro null
non sarebbe necessariamente ridondante qui.]
Soluzione
Diversi gruppi hanno standard diversi.
In primo luogo, presumo che tu conosca la differenza tra RuntimeException
s (non selezionata) e Eccezione
normale (selezionata), se non poi vedi questa domanda e le risposte . Se scrivi la tua eccezione, puoi forzare il rilevamento, mentre sia NullPointerException
che IllegalArgumentException
sono RuntimeExceptions che sono disapprovati in alcuni ambienti.
In secondo luogo, come con te, i gruppi con cui ho collaborato ma non usano attivamente assert, ma se il tuo team (o consumatore dell'API) ha deciso che utilizzerà assert, allora asserisce che i suoni sono esattamente come il meccanismo corretto.
Se fossi in te userei NullPointerException
. La ragione di ciò è precedente. Prendi un esempio API Java da Sun, ad esempio java.util.TreeSet . Questo utilizza NPE proprio per questo tipo di situazione, e mentre sembra che il tuo codice abbia appena usato un valore null, è del tutto appropriato.
Come altri hanno affermato IllegalArgumentException
è un'opzione, ma penso che NullPointerException sia più comunicativo.
Se questa API è progettata per essere utilizzata da società / team esterni, rimarrei con NullPointerException
, ma assicurati che sia dichiarata in javadoc. Se è per uso interno, potresti decidere che vale la pena aggiungere la tua stessa gerarchia di eccezioni, ma personalmente trovo che le API che aggiungono enormi gerarchie di eccezioni, che saranno solo printStackTrace ()
d o registrate sono solo uno spreco di sforzi.
Alla fine della giornata, la cosa principale è che il tuo codice comunica chiaramente. L'erarchia di un'eccezione locale è come il gergo locale: aggiunge informazioni per gli addetti ai lavori ma può confondere gli estranei.
Per quanto riguarda il controllo contro null, direi che ha senso. In primo luogo, ti consente di aggiungere un messaggio su ciò che era null (ovvero nodo o token) quando costruisci l'eccezione che sarebbe utile. In secondo luogo, in futuro potresti utilizzare un'implementazione di Map
che consente null
, quindi perderai il controllo degli errori. Il costo è quasi nulla, quindi a meno che un profiler non dica che si tratta di un problema di circuito interno, non me ne preoccuperei.
Altri suggerimenti
L'eccezione Java standard è IllegalArgumentException
. Alcuni lanceranno NullPointerException
se l'argomento è nullo, ma per me NPE ha quel "qualcuno che ha rovinato". connotazione e non vuoi che i clienti della tua API pensino di non sapere cosa stai facendo.
Per le API pubbliche, controlla gli argomenti e fallisci in modo tempestivo e pulito. Il tempo / il costo conta a malapena.
In Java normalmente si genera un'eccezione IllegalArgumentException
Se vuoi una guida su come scrivere un buon codice Java, posso consigliare vivamente il libro Efficace Java di Joshua Bloch.
Sembra che questo potrebbe essere un uso appropriato per un assert :
public void setTokens(Node node, int newTokens) {
assert node != null;
tokens.put(node, newTokens);
}
Il tuo approccio dipende interamente dal contratto che la tua funzione offre ai chiamanti: è un prerequisito che il nodo non sia nullo?
Se lo è, dovresti generare un'eccezione se il nodo è null, poiché si tratta di una violazione del contratto. In caso contrario, la tua funzione dovrebbe gestire in silenzio il nodo null e rispondere in modo appropriato.
Penso che molto dipenda dal contratto del metodo e dalla conoscenza del chiamante.
Ad un certo punto del processo, il chiamante potrebbe agire per convalidare il nodo prima di chiamare il metodo. Se conosci il chiamante e sai che questi nodi sono sempre convalidati, penso che sia giusto supporre che otterrai buoni dati. Essenzialmente la responsabilità è sul chiamante.
Tuttavia, ad esempio, se si sta fornendo una libreria di terze parti distribuita, è necessario convalidare il nodo per valori null, ecc.
Un illegalArugementException è lo standard java ma è anche RunTimeException. Pertanto, se si desidera forzare il chiamante a gestire l'eccezione, è necessario fornire un'eccezione di controllo, probabilmente una personalizzata che si crea.
Personalmente, vorrei che NullPointerExceptions si verificasse SOLO per caso, quindi è necessario utilizzare qualcos'altro per indicare che è stato passato un valore di argomento illegale. IllegalArgumentException va bene per questo.
if (arg1 == null) {
throw new IllegalArgumentException("arg1 == null");
}
Questo dovrebbe essere sufficiente sia per coloro che leggono il codice, ma anche per la povera anima che riceve una chiamata di supporto alle 3 del mattino.
(e, SEMPRE fornire un testo esplicativo per le tue eccezioni, le apprezzerai un giorno triste)
come l'altro: java.lang.IllegalArgumentException. Informazioni sul controllo del nodo null, che dire del controllo di input errati durante la creazione del nodo?
Non devo piacere a nessuno, quindi quello che faccio ora come codice canonico è
void method(String s)
if((s != null) && (s instanceof String) && (s.length() > 0x0000))
{
che mi fa dormire molto.
Altri non saranno d'accordo.