Cosa è meglio IllegalStateException o esecuzione silenziosa del metodo? [chiuso]

15

Diciamo che ho una classe MediaPlayer con metodi play () e stop (). Qual è la migliore strategia da utilizzare quando si implementa il metodo stop nel caso in cui il metodo di gioco non sia stato chiamato prima. Vedo due opzioni: lanciare un'eccezione perché il player non è nello stato appropriato o ignorare silenziosamente le chiamate al metodo stop.

Quale dovrebbe essere la regola generale quando un metodo non dovrebbe essere chiamato in alcune situazioni, ma la sua esecuzione non danneggia il programma in generale?

    
posta x2bool 09.05.2016 - 15:14
fonte

11 risposte

37

Non c'è una regola. Dipende interamente da come vuoi che la tua API "si senta".

Personalmente, in un lettore musicale, penso che una transizione dallo stato Stopped a Stopped con il metodo Stop() sia una transizione di stato perfettamente valida. Non è molto significativo, ma è valido. Con questo in mente, lanciare un'eccezione sembrerebbe pedante e ingiusto. Renderebbe l'API come l'equivalente sociale di parlare con il ragazzo fastidioso sul bus della scuola. Sei bloccato con la situazione fastidiosa, ma puoi occupartene.

Un approccio più "socievole" è riconoscere che la transizione è nella peggiore delle ipotesi innocua ed essere gentile con gli sviluppatori che lo consumano permettendoglielo.

Quindi, se dipendesse da me, salterò l'eccezione.

    
risposta data 09.05.2016 - 15:27
fonte
17

Ci sono due diversi tipi di azioni che si potrebbero desiderare di eseguire:

  1. Verifica contemporaneamente che qualcosa sia in uno stato e cambialo in un altro.

  2. Imposta qualcosa su un particolare stato, senza riguardo per lo stato precedente.

Alcuni contesti richiedono un'azione e alcuni richiedono l'altro. Se un lettore multimediale che raggiunge la fine del contenuto rimarrà in uno stato di "riproduzione", ma con la posizione congelata alla fine, allora un metodo che asserisce simultaneamente che il lettore era in uno stato di riproduzione mentre lo impostava su "stop" lo stato potrebbe essere utile se il codice desidera assicurarsi che nulla abbia causato il fallimento della riproduzione prima della richiesta di interruzione (che potrebbe essere importante se ad esempio qualcosa stava registrando il contenuto riprodotto come mezzo per convertire il file multimediale da un formato a un altro) . Nella maggior parte dei casi, tuttavia, ciò che conta è che dopo l'operazione il lettore multimediale si trovi nello stato previsto (cioè fermo).

Se un lettore multimediale si arresta automaticamente quando raggiunge la fine del supporto, una funzione che asserisce che il lettore era in esecuzione sarebbe probabilmente più fastidioso che utile, ma in alcuni casi sapendo se il giocatore stava correndo nel momento in cui è stato fermato potrebbe essere utile. Forse l'approccio migliore per soddisfare entrambe le esigenze sarebbe quello di far restituire alla funzione un valore che indica lo stato precedente del giocatore. Il codice che si preoccupa di quello stato potrebbe esaminare il valore di ritorno; codice che non interessa potrebbe semplicemente ignorarlo.

    
risposta data 09.05.2016 - 18:50
fonte
11

Non esiste una regola generale. In questo caso specifico, l'intenzione dell'utente della tua API è di impedire al giocatore di riprodurre i media. Se il lettore non sta riproducendo il media, MediaPlayer.stop() non può fare nulla e l'obiettivo del metodo caller sarà comunque raggiunto - il media non sta giocando.

Se si lancia un'eccezione, l'utente dell'API deve verificare se il giocatore sta giocando o catturare e gestire l'eccezione. Ciò renderebbe l'API più di una fatica da usare.

    
risposta data 09.05.2016 - 16:16
fonte
11

Lo scopo delle eccezioni non è quello di segnalare che qualcosa di brutto è successo; è per segnalare che

  1. È successo qualcosa di brutto
  2. che non so come risolvere qui
  3. che il chiamante, o qualcosa di simile sul callstack da esso, dovrebbe sapere come gestire
  4. quindi mi sto liberando rapidamente, interrompendo l'esecuzione dell'attuale percorso del codice per evitare danni o danneggiamento dei dati, e lasciandolo al chiamante per ripulire

Se il chiamante prova a Stop a MediaPlayer che è già in uno stato arrestato, questo non è un problema che MediaPlayer non può risolvere (può semplicemente non fare nulla). Non è un problema che, se continuato, porterà a danni o corruzione dei dati, dal momento che può avere successo semplicemente facendo nulla. E non è davvero un problema che sia più ragionevole aspettarsi che il chiamante sia in grado di risolvere oltre il MediaPlayer .

Pertanto, non si dovrebbe generare un'eccezione in questa situazione.

    
risposta data 09.05.2016 - 21:33
fonte
5

Guarda in questo modo:

Se il client chiama Stop () quando il giocatore non sta giocando, Stop () ha successo automaticamente perché il giocatore è attualmente in stato di arresto.

    
risposta data 09.05.2016 - 16:36
fonte
3

La regola è che fai ciò che richiede il contratto del tuo metodo.

Riesco a vedere più modi per definire contratti significativi per tale metodo stop . Potrebbe essere perfettamente valido per il metodo stop per fare assolutamente nulla se il giocatore non sta giocando. In tal caso, definisci la tua API in base ai suoi obiettivi. Vuoi passare allo stato stopped , e il metodo stop lo fa, semplicemente passando dallo stato stopped a se stesso senza errore.

C'è qualche motivo per cui vuoi lanciare un'eccezione? Se il codice utente sarà simile alla fine:

try {
    player.stop();
} catch(IllegalStateException e) {
    // do nothing, player was already stopped
}

quindi non c'è alcun vantaggio nell'avere quell'eccezione. Quindi la domanda è: l'utente si preoccuperà del fatto che il giocatore sia già stato fermato? Ha un altro wa per interrogarlo?

Il giocatore potrebbe essere osservabile e potrebbe già informare gli osservatori su alcune cose, ad es. avvisare gli osservatori quando transita da playing a stopping a stopped . In questo caso, non è necessario avere il metodo di lancio di un'eccezione. Chiamando stop non farai nulla se il giocatore è già fermo e chiamandolo quando non è fermato notificherà agli osservatori la transizione.

Tutto sommato, dipende da dove finirà la tua logica. Ma penso che ci siano scelte progettuali migliori per poi lanciare un'eccezione.

Dovresti lanciare un'eccezione se il chiamante deve intervenire. In questo caso non è necessario, puoi semplicemente lasciare che il giocatore sia come è e continua a funzionare.

    
risposta data 09.05.2016 - 21:02
fonte
3

Esiste una semplice strategia generale che ti aiuta a prendere questa decisione.

Considera come verrà gestita l'eccezione se dovesse essere lanciata.

Quindi immagina il tuo lettore musicale, i clic dell'utente si fermano e poi si fermano di nuovo. Vorresti visualizzare un messaggio di errore in quello scenario? Non ho ancora visto un giocatore che lo faccia. Vuoi che il comportamento dell'applicazione sia diverso dal fare clic una volta per interrompere (come registrare l'evento o inviare un rapporto di errore in background)? Probabilmente no.

Ciò significa che l'eccezione dovrebbe essere catturata e ingoiata da qualche parte. Ciò significa che stai meglio non lanciare l'eccezione in primo luogo perché non vuoi intraprendere un'azione diversa .

Questo non si applica solo alle eccezioni, ma a qualsiasi tipo di diramazione: in pratica, se non ci deve essere alcuna differenza osservabile nel comportamento, non c'è bisogno di ramificazioni.

Detto questo, non c'è molto danno nel definire il tuo metodo stop() per restituire un boolean o un valore enum che indica se l'arresto è "riuscito". Probabilmente non lo userai mai, ma un valore di ritorno può essere più facilmente e naturalmente ignorato di un'eccezione.

    
risposta data 10.05.2016 - 11:51
fonte
2

Ecco come funziona Android: MediaPlayer

In poche parole, stop quando start non è stato chiamato non è un problema, il sistema rimane in stato di arresto, ma se chiamato su un giocatore che non sa nemmeno cosa sta giocando, viene lanciata un'eccezione , perché non c'è una buona ragione per chiamare fermarsi qui.

    
risposta data 09.05.2016 - 22:51
fonte
1

What should be the general rule when a method is not supposed to be called in some situation, but it's execution does no harm to the program in general?

Vedo quale potrebbe essere una piccola contraddizione nella tua affermazione che potrebbe rendere la risposta chiara a te. Perché il metodo non dovrebbe essere chiamato e tuttavia la sua esecuzione non fa male ?

Perché il metodo "non dovrebbe essere chiamato"? Sembra una restrizione autoimposta. Se non c'è alcun "danno" o impatto sulla tua API, allora non c'è un'eccezione. Se il metodo in realtà non dovrebbe essere chiamato perché potrebbe creare stati non validi o imprevedibili, allora dovrebbe essere generata un'eccezione.

Ad esempio, se raggiungo un lettore DVD e prendo "stop" prima di colpire "start" e si è schiantato, non avrebbe senso per me. Dovrebbe semplicemente sedersi lì o, peggio, riconoscere "stop" sullo schermo. Un errore sarebbe fastidioso e non utile in alcun modo.

Tuttavia, posso inserire un codice di allarme per disattivarlo quando è già spento (chiamando il metodo), ciò potrebbe far sì che esso entri in uno stato che impedirebbe di inserirlo correttamente in seguito? Se è così, allora genera un errore anche se, tecnicamente, "non c'è stato alcun danno" perché potrebbe esserci un problema in seguito. Vorrei sapere di questo anche se in realtà non è andato in quello stato. Solo la possibilità è sufficiente. Questo sarebbe un IllegalStateException .

Nel tuo caso, se la tua macchina di stato può gestire la chiamata non valida / non necessaria, ignorala, altrimenti genera un errore.

EDIT:

Si noti che un compilatore non richiama un errore se si imposta una variabile due volte senza ispezionare il valore. Inoltre, non ti impedisce di reinizializzare una variabile. Ci sono molte azioni che possono essere considerate un "bug" da una prospettiva, ma sono semplicemente "inefficienti", "inutili", "inutili", ecc. Ho cercato di fornire quella prospettiva nella mia risposta - fare queste cose non è generalmente considerato un "bug" perché non risulta in qualcosa di inaspettato. Probabilmente dovresti affrontare il tuo problema in modo simile.

    
risposta data 09.05.2016 - 21:40
fonte
1

Che cosa fanno esattamente MediaPlayer.play() e MediaPlayer.stop() ? - Sono evento ascoltatori per input dell'utente o sono in realtà metodi che avviano una sorta di flusso multimediale sul sistema? Se tutto ciò che sono sono ascoltatori per l'input dell'utente, è perfettamente ragionevole per loro non fare nulla (anche se sarebbe una buona idea almeno registrarli da qualche parte). Tuttavia, se influenzano il modello che sta controllando l'interfaccia utente, allora potrebbe lanciare un IllegalStateException perché il giocatore dovrebbe avere una sorta di isPlaying=true o isPlaying=false stato (non deve essere un vero flag booleano come scritto qui), e quindi quando chiamate MediaPlayer.stop() quando isPlaying=false , il metodo non può effettivamente "fermare" il MediaPlayer perché l'oggetto non è nello stato appropriato da arrestare: vedere la descrizione di java.lang.IllegalStateException classe:

Signals that a method has been invoked at an illegal or inappropriate time. In other words, the Java environment or Java application is not in an appropriate state for the requested operation.

Perché le eccezioni di lancio possono essere buone

Molte persone sembrano pensare che le eccezioni siano in generale cattive, in quanto non dovrebbero mai essere generate (cfr. Joel on Software ). Tuttavia, supponi di avere un MediaPlayerGUI.notifyPlayButton() che chiama MediaPlayer.play() e MediaPlayer.play() richiama un gruppo di altri codici e da qualche parte lungo la linea si interfaccia con es. PulseAudio , ma io (lo sviluppatore) non so dove, perché non ho scritto tutto quel codice.

Poi, un giorno mentre lavoro sul codice, clicco su "riproduci" e non succede nulla. Se MediaPlayerGUIController.notifyPlayButton() registra qualcosa, almeno posso esaminare i log e controllare che il clic del pulsante sia stato effettivamente registrato ... ma perché non funziona? Beh, diciamo che in effetti c'è qualcosa di sbagliato nei wrapper PulseAudio che MediaPlayer.play() richiama. Faccio di nuovo clic sul pulsante "Riproduci" e questa volta ottengo un IllegalStateException :

 Exception in thread "main" java.lang.IllegalStateException: MediaPlayer already in play state.

     at org.superdupermediaplayer.MediaPlayer.play(MediaPlayer.java:16)
     at org.superdupermediaplayer.gui.MediaPlayerGUIController.notifyPlayButton(MediaPlayerGUIController.java:25)
     at org.superdupermediaplayer.gui.MediaPlayerGUI.main(MediaPlayerGUI.java:14)

Osservando la traccia dello stack, posso ignorare tutto prima di MediaPlayer.play() durante il debug e dedicare il mio tempo a capire perché, ad es. nessun messaggio viene passato a PulseAudio per avviare un flusso audio.

D'altra parte, se non fai un'eccezione, non avrò nulla per iniziare a lavorare con altro che: "Lo stupido programma non suona musica"; Sebbene non sia così brutto come il errore nascosto come in realtà ingoiando un'eccezione che è in effetti generata , stai ancora potenzialmente sprecando il tempo degli altri quando qualcosa va sbagliato ... quindi sii gentile e lancia loro un'eccezione.

    
risposta data 09.05.2016 - 15:26
fonte
0

Preferisco progettare l'API in modo da rendere più difficile o impossibile per il consumatore commettere errori. Ad esempio, invece di avere MediaPlayer.play() e MediaPlayer.stop() , potresti fornire MediaPlayer.playToggle() che alterna tra "fermato" e "in riproduzione". In questo modo, il metodo è sempre sicuro da chiamare, non c'è il rischio di entrare in uno stato illegale.

Naturalmente, questo non è sempre possibile o facile da fare. L'esempio che hai fornito è paragonabile a provare a rimuovere un elemento che è già stato rimosso dall'elenco. Puoi essere

  • pragmatico al riguardo e non genera alcun errore. Questo certamente semplifica molto codice, ad es., Invece di dover scrivere if (list.contains(x)) { list.remove(x) } hai solo bisogno di list.remove(x) ). Ma può anche nascondere i bug.
  • oppure puoi essere pedante e segnalare un errore che l'elenco non contiene quell'elemento. Il codice può diventare più complicato a volte, poiché devi verificare costantemente se le pre-condizioni sono soddisfatte prima di chiamare il metodo, ma il lato positivo è che eventuali bug sono più facili da rintracciare.

Se chiamare MediaPlayer.stop() quando è già stato arrestato non ha alcun danno nell'applicazione, quindi lo lascio girare silenziosamente perché semplifica il codice e ho una cosa per i metodi idempotenti. Ma se sei assolutamente certo che MediaPlayer.stop() non verrebbe mai chiamato in quelle condizioni, allora genererei un errore perché potrebbe esserci un bug da qualche altra parte nel codice e l'eccezione potrebbe aiutare a rintracciarlo.

    
risposta data 09.05.2016 - 19:41
fonte

Leggi altre domande sui tag