¿Qué es mejor IllegalStateException o la ejecución silenciosa de métodos? [cerrado]

15

Digamos que tengo una clase de MediaPlayer que tiene los métodos play () y stop (). ¿Cuál es la mejor estrategia a utilizar cuando se implementa el método de detención en caso de que el método de reproducción no haya sido llamado antes? Veo dos opciones: lanzar una excepción porque el jugador no está en el estado adecuado, o ignorar silenciosamente las llamadas al método de detención.

¿Cuál debería ser la regla general cuando se supone que no se debe llamar a un método en alguna situación, pero su ejecución no perjudica al programa en general?

    
pregunta x2bool 09.05.2016 - 15:14

11 respuestas

37

No hay regla. Depende de cómo quieres que se sienta tu API ".

Personalmente, en un reproductor de música, creo que una transición del estado Stopped a Stopped mediante el método Stop() es una transición de estado perfectamente válida. No es muy significativo, pero es válido. Con esto en mente, lanzar una excepción parece pedante e injusto. Haría que el API se sintiera como el equivalente social de hablar con el niño molesto en el autobús escolar. Estás atascado con la molesta situación, pero puedes lidiar con ella.

Un enfoque más "sociable" es reconocer que la transición es, en el peor de los casos, inofensiva y ser amable con los desarrolladores que lo consumen al permitirlo.

Entonces, si dependiera de mí, me saltearía la excepción.

    
respondido por el MetaFight 09.05.2016 - 15:27
17

Hay dos tipos distintos de acciones que uno podría desear realizar:

  1. Simultáneamente, compruebe que algo está en un estado y cámbielo a otro.

  2. Establezca algo en un estado particular, sin tener en cuenta el estado anterior.

Algunos contextos requieren una acción, y algunos requieren la otra. Si un reproductor multimedia que llega al final del contenido permanecerá en un estado de "reproducción", pero con la posición congelada al final, entonces un método que simultáneamente afirma que el reproductor estaba en un estado de reproducción mientras lo configuraba en una "parada" El estado podría ser útil si el código querría asegurarse de que nada haya provocado que la reproducción falle antes de la solicitud de detención (lo que podría ser importante si, por ejemplo, algo estaba grabando el contenido que se está reproduciendo como un medio para convertir los medios de un formato a otro) . Sin embargo, en la mayoría de los casos, lo que importa es que después de la operación, el reproductor multimedia se encuentra en el estado esperado (es decir, detenido).

Si un reproductor multimedia se detiene automáticamente cuando llega al final de los medios, una función que afirma que el reproductor estaba funcionando probablemente sería más molesta que útil, pero en algunos casos, saber si el reproductor estaba funcionando en ese momento. fue detenido podría ser útil. Quizás el mejor enfoque para satisfacer ambas necesidades sería que la función devuelva un valor que indique el estado anterior del jugador. El código que se preocupa por ese estado podría examinar el valor de retorno; El código que no le importa podría simplemente ignorarlo.

    
respondido por el supercat 09.05.2016 - 18:50
11

No hay una regla general. En este caso específico, la intención del usuario de su API es impedir que el reproductor reproduzca los medios. Si el jugador no está reproduciendo los medios, el MediaPlayer.stop() puede no hacer nada y el objetivo de la persona que llama aún se logrará: los medios no se están reproduciendo.

Lanzar una excepción requeriría que el usuario de la API verifique si el jugador está jugando actualmente o atrape y maneje la excepción. Esto haría que la API sea más una tarea de usar.

    
respondido por el kmaczuga 09.05.2016 - 16:16
11

El propósito de las excepciones no es señalar que algo malo ha sucedido; es para señalar que

  1. Algo malo ha sucedido
  2. que no sé cómo solucionar aquí
  3. que la persona que llama, o algo más arriba de la pila de llamadas, debe saber cómo lidiar con
  4. por lo que estoy rescatando rápidamente, deteniendo la ejecución de la ruta del código actual para evitar daños o daños en los datos, y dejar que la persona que llama haga la limpieza

Si la persona que llama intenta Stop a MediaPlayer que ya está en estado detenido, esto no es un problema que el MediaPlayer no pueda resolver (simplemente no puede hacer nada). No es un problema. problema que, de continuar, provocará daños o daños en los datos, ya que puede tener éxito simplemente al no hacer nada. Y no es realmente un problema que sea más razonable esperar que la persona que llama pueda resolver que el MediaPlayer .

Por lo tanto, no debes lanzar una excepción en esta situación.

    
respondido por el Mason Wheeler 09.05.2016 - 21:33
5

Míralo de esta manera:

Si el cliente llama a Stop () cuando el jugador no está jugando, entonces Stop () tiene éxito automáticamente porque el jugador se encuentra en el estado detenido.

    
respondido por el 17 of 26 09.05.2016 - 16:36
3

La regla es que hagas lo que requiere el contrato de tu método.

Puedo ver varias formas de definir contratos significativos para dicho método stop . Podría ser perfectamente válido que el método stop no haga absolutamente nada si el jugador no está jugando. En ese caso, usted define su API por sus objetivos. Desea realizar la transición al estado stopped , y el método stop hace eso, solo mediante la transición del estado stopped a sí mismo sin error.

¿Hay alguna razón por la que desea lanzar una excepción? Si el código de usuario se verá así al final:

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

entonces no hay beneficio de tener esa excepción. Entonces la pregunta es, ¿le importará al usuario el hecho de que el jugador ya se haya detenido? ¿Tiene otro wa para consultarlo?

El jugador puede ser observable y ya puede notificar a los observadores sobre ciertas cosas, por ejemplo, notificando a los observadores cuando pasa de playing a stopping a stopped . En este caso, hacer que el método arroje una excepción no es necesario. Llamar a stop simplemente no hará nada si el jugador ya está detenido, y si lo llama cuando no se detiene, notificará a los observadores sobre la transición.

en general, depende de dónde terminará tu lógica. Pero creo que hay mejores opciones de diseño que lanzar una excepción.

Debes lanzar una excepción si el llamante tiene que intervenir. En este caso, no tiene que hacerlo, simplemente puedes dejar que el jugador esté como está y sigue funcionando.

    
respondido por el Polygnome 09.05.2016 - 21:02
3

Hay una estrategia general simple que te ayuda a tomar esta decisión.

Considere cómo se manejaría la excepción si se lanzara.

Así que imagina tu reproductor de música, los clics del usuario se detienen y luego se detienen nuevamente. ¿Desea mostrar un mensaje de error en ese escenario? Todavía no he visto un jugador que lo haga. ¿Desea que el comportamiento de la aplicación sea diferente al solo hacer clic en detener una vez (como registrar el evento o enviar un informe de errores en segundo plano)? Probablemente no.

Eso significa que la excepción debería ser atrapada y tragada en algún lugar. Y eso significa que es mejor no lanzar la excepción en primer lugar porque no quiere realizar una acción diferente .

Esto no solo se aplica a las excepciones, sino a cualquier tipo de ramificación: básicamente, si no es necesario que exista una diferencia observable en el comportamiento, no hay necesidad de ramificación.

Dicho esto, no hay mucho daño en la definición del método stop() para devolver un boolean o un valor enum que indique si la detención fue "exitosa". Probablemente nunca lo usará, pero un valor de retorno puede ser ignorado más fácil y naturalmente que una excepción.

    
respondido por el biziclop 10.05.2016 - 11:51
2

Aquí está cómo lo hace Android: MediaPlayer

En pocas palabras, stop cuando no se ha llamado a start no es un problema, el sistema permanece en estado detenido, pero si se le llama a un jugador que ni siquiera sabe qué está jugando, se lanza una excepción , porque no hay una buena razón para llamar a parar allí.

    
respondido por el njzk2 09.05.2016 - 22:51
1
  

¿Cuál debería ser la regla general cuando se supone que un método no es   Llamado en alguna situación, pero su ejecución no hace daño a la   programa en general?

Veo lo que podría ser una pequeña contradicción en su declaración que pueda aclararle la respuesta. ¿Por qué es que el método no debe llamarse y, sin embargo, su ejecución no perjudica ?

¿Por qué el método "no debe llamarse"? Eso parece una restricción autoimpuesta. Si no hay un "daño" o impacto en su API, entonces no hay una excepción. Si realmente no se debe llamar al método porque puede crear estados no válidos o impredecibles, se debe lanzar una excepción.

Por ejemplo, si me acerqué a un reproductor de DVD y presioné "stop" antes de presionar "start" y se estrelló, eso no tendría sentido para mí. Simplemente debe sentarse allí o, en el peor de los casos, reconocer "parar" en la pantalla. Un error sería molesto y no sería útil de ninguna manera.

Sin embargo, ¿puedo poner un código de alarma para apagarlo cuando ya está apagado (llamando al método), lo que puede provocar que ingrese en un estado que impida que lo active correctamente más tarde? Si es así, lance un error aunque, técnicamente, "no hubo daño" porque puede haber un problema más adelante. Me gustaría saber acerca de esto, incluso si realmente no entró en ese estado. Solo la posibilidad es suficiente. Esto sería un IllegalStateException .

En su caso, si su máquina de estado puede manejar la llamada no válida / innecesaria, entonces ignórela, de lo contrario arroje un error.

EDITAR:

Tenga en cuenta que un compilador no invoca un error si establece una variable dos veces sin inspeccionar el valor. Tampoco le impide reiniciar una variable. Hay muchas acciones que podrían considerarse un "error" desde una perspectiva, pero son meramente "ineficientes", "innecesarias", "inútiles", etc. Traté de proporcionar esa perspectiva en mi respuesta; generalmente no se considera hacer estas cosas un "error" porque no resulta en nada inesperado. Probablemente debería abordar su problema de manera similar.

    
respondido por el Jim 09.05.2016 - 21:40
1

¿Qué hacen exactamente MediaPlayer.play() y MediaPlayer.stop() ? - ¿Son evento oyentes para ¿La entrada del usuario o en realidad son métodos que inician algún tipo de flujo de medios en el sistema? Si todo lo que son son escuchas para la entrada del usuario, entonces es perfectamente razonable que no hagan nada (aunque sería una buena idea al menos registrarlos en algún lugar). Sin embargo, si afectan el modelo que controla su interfaz de usuario, entonces podría lanzar un IllegalStateException porque el jugador debería tener algún tipo de isPlaying=true o isPlaying=false (no es necesario que sea una marca booleana real como la que se escribe aquí), por lo que cuando llama a MediaPlayer.stop() cuando isPlaying=false , el método no puede "detener" el MediaPlayer porque el objeto no se encuentra en el estado adecuado para detenerlo: consulte la descripción de java.lang.IllegalStateException class:

  

Señala que un método ha sido invocado en un momento ilegal o inapropiado. En otras palabras, el entorno Java o la aplicación Java no se encuentran en el estado adecuado para la operación solicitada.

Por qué lanzar excepciones puede ser bueno

Mucha gente parece pensar que las excepciones son malas en general, ya que nunca deberían lanzarse (cf. Joel en Software ). Sin embargo, supongamos que tiene un MediaPlayerGUI.notifyPlayButton() que llama a MediaPlayer.play() , y MediaPlayer.play() invoca un montón de otro código y en algún lugar de la línea con la que interactúa, por ejemplo. PulseAudio , pero yo (el desarrollador) no sé dónde porque no escribí todo ese código.

Luego, un día mientras trabajaba en el código, hago clic en "jugar" y no pasa nada. Si MediaPlayerGUIController.notifyPlayButton() registra algo, al menos puedo mirar los registros y verificar que el clic del botón se haya registrado de hecho ... pero ¿por qué no se reproduce? Bueno, digamos que, de hecho, hay algo mal con las envolturas PulseAudio que invoca MediaPlayer.play() . Vuelvo a hacer clic en el botón "jugar", y esta vez obtengo 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)

Al observar el seguimiento de la pila, puedo ignorar todo antes de MediaPlayer.play() al realizar la depuración y pasar mi tiempo averiguando por qué, por ejemplo. no se pasa ningún mensaje a PulseAudio para iniciar una transmisión de audio.

Por otra parte, si no lanza una excepción, no tendré nada con lo que empezar a trabajar que no sea: "El programa estúpido no reproduce ninguna música"; Aunque no es tan malo como explícito ocultación de errores como en realidad tragar una excepción que fue en realidad lanzada , todavía puede estar perdiendo el tiempo de otros cuando algo sucede mal ... así que sé amable y hazles una excepción.

    
respondido por el errantlinguist 09.05.2016 - 15:26
0

Prefiero diseñar la API de manera que sea más difícil o imposible para el consumidor cometer errores. Por ejemplo, en lugar de tener MediaPlayer.play() y MediaPlayer.stop() , podría proporcionar MediaPlayer.playToggle() que alterna entre "detenido" y "en reproducción". De esta manera, siempre es seguro llamar al método, no hay riesgo de entrar en un estado ilegal.

Por supuesto, esto no siempre es posible o fácil de hacer. El ejemplo que dio es comparable a intentar eliminar un elemento que ya se eliminó de la lista. Usted puede ser

  • pragmático al respecto y no lanzar ningún error. Esto ciertamente simplifica una gran cantidad de código, por ejemplo, en lugar de tener que escribir if (list.contains(x)) { list.remove(x) } , solo necesita list.remove(x) ). Pero, también puede ocultar errores.
  • o puede ser pedante y señalar un error que indica que la lista no contiene ese elemento. El código puede complicarse a veces, ya que constantemente tiene que verificar si se cumplen las condiciones previas antes de llamar al método, pero la ventaja es que los posibles errores son más fáciles de localizar.

Si llamar a MediaPlayer.stop() cuando ya está detenido no tiene ningún problema en tu aplicación, entonces lo dejaría correr en silencio porque simplifica el código y tengo una cosa con los métodos idempotentes. Pero si está absolutamente seguro de que nunca se llamará a MediaPlayer.stop() en esas condiciones, entonces cometeré un error porque podría haber un error en otra parte del código y la excepción lo ayudará a localizarlo.

    
respondido por el Pedro Rodrigues 09.05.2016 - 19:41

Lea otras preguntas en las etiquetas