Quando è corretto utilizzare la gestione delle eccezioni per la business logic?

Penso che sia accettato che come regola generale in Java (e forse in qualsiasi linguaggio con gestione delle eccezioni) si dovrebbe cercare di evitare l’uso della gestione delle eccezioni per gestire effettivamente la logica di business. In generale, se è previsto che si verifichi una determinata situazione, è necessario verificarla e gestirla più direttamente che affidarsi alla gestione delle eccezioni per eseguire il controllo. Ad esempio, il seguente non è considerato una buona pratica:

try{ _map.put(myKey, myValue); } catch(NullPointerException e){ _map = new HashMap(); } 

L’inizializzazione piuttosto pigra dovrebbe essere realizzata più come questa:

 if(_map == null){ _map = new HashMap(); } _map.put(myKey, myValue); 

Naturalmente ci potrebbe essere una logica molto più complessa della semplice gestione dell’inizializzazione pigra. Quindi, dato che questo tipo di cose è solitamente disapprovato … quando, se mai, è una buona idea contare su un’eccezione che si verifica per determinate logiche di business? Sarebbe corretto dire che qualsiasi istanza in cui ci si sente obbligati a utilizzare questo approccio sta davvero evidenziando una debolezza dell’API utilizzata?

Ogni volta che l’eccezione può essere anticipata ma non evitata.

Supponiamo, se si sta facendo affidamento su un’API esterna di qualche tipo per analizzare i dati, e che l’API offre metodi di analisi ma nulla da dire se un determinato input può essere analizzato o meno (o se l’analisi può avere esito positivo o meno dipende da fattori esterni del tuo controllo, ma l’API non fornisce chiamate di funzione appropriate) e il metodo di analisi genera un’eccezione quando l’input non può essere analizzato.

Con un’API opportunamente progettata, questo dovrebbe ridursi a una quantità da qualche parte nell’intervallo “virtualmente mai” a “mai”.

Non vedo assolutamente alcun motivo per utilizzare la gestione delle eccezioni come mezzo per il normale controllo del stream nel codice. È costoso, è difficile da leggere (guarda solo il tuo primo esempio, mi rendo conto che probabilmente è stato scritto molto velocemente, ma quando _map non è stato inizializzato, ciò che _map è una mappa vuota, buttando via la voce che eri cercando di aggiungere), e il codice si allenta con blocchi try-catch in gran parte inutili, che possono benissimo hide i problemi reali . Di nuovo prendendo il tuo esempio, e se la chiamata a _map.add() dovesse lanciare una NullPointerException per qualche motivo diverso da _map essere null ? All’improvviso, stai ricreando in silenzio una mappa vuota anziché aggiungere una voce. Che sono sicuro di non avere da dire, può portare a un numero qualsiasi di bug in posti completamente indipendenti nel codice a causa di uno stato imprevisto …

Modifica: Per essere chiari, la risposta sopra è scritta nel contesto di Java. Altre lingue potrebbero (e apparentemente fare) differire nella spesa di implementazione delle eccezioni, ma altri punti dovrebbero ancora valere.

Lanciare un’eccezione è un’operazione relativamente costosa in C ++ ed estremamente costosa in Java. Solo per motivi di efficienza, non ha mai senso evitare un controllo esplicito e accettare invece un’eccezione. Suppongo che potresti essere in grado di giustificarlo in alcuni rari casi in cui controllare se un’eccezione sarebbe stata lanciata è molto complessa o quasi imansible, ma in caso contrario, direi che la risposta è “praticamente mai”.

Questa è davvero una domanda di discussione e la risposta dipende dal design del tuo sistema.

Il mio istinto è sempre una coperta mai, ma ho visto diversi sistemi implementare errori di business usando le eccezioni. Personalmente lo trovo disgustoso, ma capisco davvero il vantaggio di interrompere un processo aziendale non appena si sa che non ha funzionato, gestire il proprio fallimento (ad es. Ripristinare la propria unità di lavoro) e restituire l’errore al chiamante, magari con l’aggiunta informazione.

uno dei possibili svantaggi è che è molto semplice gestire gli errori su più classi diverse, così che la definizione di cosa succede quando il processo fallisce è davvero difficile da dedurre dal codice.

In ogni caso non c’è una risposta singola qui, è necessario valutare entrambi gli approcci e, a volte, combinarli.

per quanto riguarda il tuo esempio, non vedo alcun vantaggio nell’usare eccezioni per il controllo del stream, specialmente in uno scenario “buono” (progettato per funzionare).

C’è un motivo per cui le eccezioni sono oggetti. C’è anche un motivo per cui i designer del linguaggio Java suddividono tutti i Throwable in 2 tipi principali: selezionato e deselezionato.

è una buona idea affidarsi a un’eccezione che si verifica per determinate logiche di business?

Sì. Assolutamente. Dovresti leggere il capitolo 9 di “Effective Java, Second Edition”. È tutto lì. Ben spiegato e in attesa per te.

Se si ha a che fare con una condizione di errore che è in realtà una parte della logica aziendale, è OK utilizzare Eccezioni. Per esempio:

 try{ // register new user if(getUser(name) != null) throw new MyAppException("Such user already exists"); //other registration steps...... }catch(MyAppException ex){ sendMessage(ex.getMessage()); }