Digamos que tengo una clase MediaPlayer que tiene métodos play () y stop (). ¿Cuál es la mejor estrategia para usar al implementar 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 apropiado o ignorar silenciosamente las llamadas al método de detención.
¿Cuál debería ser la regla general cuando no se debe llamar a un método en alguna situación, pero su ejecución no daña el programa en general?
exceptions
methods
state
x2bool
fuente
fuente
Respuestas:
No hay regla Depende totalmente de cómo desea que su API "se sienta".
Personalmente, en un reproductor de música, creo que una transición del estado
Stopped
aStopped
través del métodoStop()
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 parecería pedante e injusto. Haría que la API parezca el equivalente social de hablar con el niño molesto en el autobús escolar. Estás atrapado 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 sus desarrolladores consumidores permitiéndola.
Entonces, si fuera por mí, me saltaría la excepción.
fuente
MediaPlayer.stop()
método que arrojó unIllegalStateException
código de depuración en lugar de pasar horas y me pregunto por qué demonios "no pasa nada" (es decir, "simplemente no funciona").StopOrThrow()
suena horrible Si vas por ese callejón, ¿por qué no usar el patrón estándar deTryStop()
? Además, en general, cuando el comportamiento de una API no está claro (como la mayoría de ellos), no se espera que los desarrolladores simplemente adivinen o experimenten, se espera que vean la documentación. Por eso me gusta tanto MSDN.Hay dos tipos distintos de acciones que uno puede desear realizar:
Simultáneamente pruebe que algo está en un estado y cámbielo a otro.
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 alcanza el final del contenido permanecerá en un estado de "reproducción", pero con la posición congelada al final, entonces un método que afirma simultáneamente que el reproductor estaba en un estado de reproducción mientras lo establece en "detener" El estado podría ser útil si el código quisiera asegurar que nada haya causado 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 esté 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 afirme que el reproductor estaba funcionando probablemente sería más molesto que útil, pero en algunos casos saber si el reproductor estaba funcionando en el momento en que se detuvo sé útil. Quizás el mejor enfoque para satisfacer ambas necesidades sería hacer 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; código que no le importa simplemente podría ignorarlo.
fuente
bool TryStop()
y Avoid Stop()
haría que esta sea una respuesta realmente excelente.TryStop
implicaría que no se debe lanzar una excepción si el jugador no puede ser forzado a un estado detenido. Tratando de detener el reproductor cuando ya está parado no es una condición excepcional, pero es una condición una persona que llama puede interesarle.isPlaying()
.No hay una regla general. En este caso específico, la intención del usuario de su API es evitar que el reproductor reproduzca los medios. Si el reproductor no reproduce los medios, es
MediaPlayer.stop()
posible que no haga nada y el objetivo de la persona que llama al método aún se alcanzará: los medios no se reproducen.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 difícil de usar.
fuente
El propósito de las excepciones no es indicar que algo malo ha sucedido; es para indicar que
Si la persona que llama intenta
Stop
un estadoMediaPlayer
que ya está en un estado detenido, este no es un problema queMediaPlayer
no puede resolver (simplemente no puede hacer nada). No es un problema que, si continúa, provocará daños o datos corrupción, ya que puede tener éxito simplemente haciendo nada. Y no es realmente un problema que sea más razonable esperar que la persona que llama pueda resolver que elMediaPlayer
.Por lo tanto, no debe lanzar una excepción en esta situación.
fuente
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 está actualmente en el estado detenido.
fuente
La regla es que haga lo que requiere el contrato de su método.
Puedo ver múltiples formas de definir contratos significativos para tal
stop
método. Podría ser perfectamente válido para elstop
método no hacer absolutamente nada si el jugador no está jugando. En ese caso, usted define su API por sus objetivos. Desea realizar la transición alstopped
estado, y elstop
método lo hace, simplemente al pasar delstopped
estado a sí mismo sin error.¿Hay alguna razón por la que quieras lanzar una Excepción? Si el código de usuario se verá así al final:
entonces no hay beneficio en tener esa excepción. Entonces, la pregunta es, ¿el usuario se preocupará por el hecho de que el jugador ya haya sido detenido? ¿Tiene otro wa para consultarlo?
El jugador puede ser observable y ya puede notificar a los observadores sobre ciertas cosas, por ejemplo, notificar a los observadores cuando transita de
playing
unstopping
a otrostopped
. En este caso, hacer que el método arroje una excepción no es necesario. Llamarstop
no hará nada si el jugador ya está detenido, y llamarlo cuando no esté parado notificará a los observadores sobre la transición.en general, depende de dónde terminará su lógica. Pero creo que hay mejores opciones de diseño que lanzar una excepción.
Debe lanzar una excepción si la persona que llama tiene que intervenir. En este caso, no tiene que hacerlo, simplemente puede dejar que el jugador esté como está y continúa funcionando.
fuente
Existe una estrategia general simple que lo ayuda a tomar esta decisión.
Considere cómo se manejaría la excepción si fuera lanzada.
Imagine su 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 a simplemente hacer clic en detener una vez (como registrar el evento o enviar un informe de error en segundo plano)? Probablemente no.
Eso significa que la excepción debería ser atrapada y tragada en alguna parte. Y eso significa que es mejor no lanzar la excepción en primer lugar porque no desea tomar una acción diferente .
Esto no solo se aplica a las excepciones, sino a cualquier tipo de ramificación: básicamente, si no necesita haber una diferencia observable en el comportamiento, no es necesario ramificar.
Dicho esto, no hay mucho daño en definir su
stop()
método para devolver unboolean
valor enum que indique si la detención fue "exitosa". Probablemente nunca lo use, pero un valor de retorno puede ignorarse de manera más fácil y natural que una excepción.fuente
Así es como lo hace Android: MediaPlayer
En pocas palabras,
stop
cuandostart
no se ha llamado no es un problema, el sistema permanece en estado detenido, pero si se llama a un jugador que ni siquiera sabe lo que está jugando, se lanza una excepción, porque no hay una buena razón para llamar detente ahí.fuente
Veo lo que podría ser una pequeña contradicción en su declaración que puede aclararle la respuesta. ¿Por qué se supone que el método no debe llamarse y, sin embargo, su ejecución no hace daño ?
¿Por qué se supone que el método "no debe llamarse"? Eso parece una restricción autoimpuesta. Si no hay "daño" o impacto en su API, entonces no hay una excepción. Si realmente no se debe invocar el 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é "detener" antes de presionar "iniciar" y se estrelló, eso no tendría sentido para mí. Debería simplemente sentarse allí o, en el peor de los casos, reconocer "parar" en la pantalla. Un error sería molesto y de ninguna manera útil.
Sin embargo, ¿puedo poner un código de alarma para "apagarlo" cuando ya está apagado (llamar al método), lo que puede provocar que entre en un estado que le impida armarlo correctamente más tarde? Si es así, arroje un error aunque, técnicamente, "no hubo daño" porque puede haber un problema más adelante. Me gustaría saber sobre esto, incluso si en realidad 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, 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 reinicializar una variable. Hay muchas acciones que podrían considerarse un "error" desde una perspectiva, pero son simplemente "ineficientes", "innecesarias", "inútiles", etc. Intenté proporcionar esa perspectiva en mi respuesta: hacer estas cosas generalmente no se considera un "error" porque no produce nada inesperado. Probablemente debería abordar su problema de manera similar.
fuente
¿Qué hacen
MediaPlayer.play()
yMediaPlayer.stop()
qué hacen exactamente ? ¿Son oyentes de eventos para la entrada del usuario o son realmente métodos que inician algún tipo de flujo de medios en el sistema? Si todos ellos son oyentes 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 a la modelo de la interfaz de usuario es el control, entonces se podría lanzar unaIllegalStateException
porque el jugador debe tener algún tipo deisPlaying=true
oisPlaying=false
estado (que no tiene por qué ser un indicador booleano real como escrito aquí), y por lo tanto cuando se llamaMediaPlayer.stop()
cuandoisPlaying=false
, el el método no puede realmente "detener" elMediaPlayer
porque el objeto no está en el estado apropiado para detenerse - vea la descripción deljava.lang.IllegalStateException
clase: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, digamos que tiene un
MediaPlayerGUI.notifyPlayButton()
que llamaMediaPlayer.play()
eMediaPlayer.play()
invoca un montón de otro código y en algún punto 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 "reproducir" y no sucede nada. Si
MediaPlayerGUIController.notifyPlayButton()
registra algo, al menos puedo mirar en los registros y comprobar que el clic del botón estaba registrado ... pero ¿por qué no se reproduce? Bueno, digamos que de hecho hay algo mal con los envoltorios PulseAudio queMediaPlayer.play()
invoca. Vuelvo a hacer clic en el botón "reproducir" y esta vez obtengo unIllegalStateException
:Al observar ese seguimiento de la pila, puedo ignorar todo antes
MediaPlayer.play()
cuando depuro y pasar mi tiempo descubriendo por qué, por ejemplo, no se pasa ningún mensaje a PulseAudio para iniciar una transmisión de audio.Por otro lado, si no lanzas una excepción, no tendré nada con lo que comenzar a trabajar aparte de: "El estúpido programa no reproduce música"; Aunque no es tan malo como ocultar un error explícito , como tragar realmente una excepción que , de hecho , se produjo , todavía está potencialmente perdiendo el tiempo de otros cuando algo sale mal ... así que sea amable y proponles una excepción.
fuente
Prefiero diseñar la API de una manera que haga que sea más difícil o imposible para el consumidor cometer errores. Por ejemplo, en lugar de tener
MediaPlayer.play()
yMediaPlayer.stop()
, podría proporcionarMediaPlayer.playToggle()
qué alterna entre "detenido" y "jugando". 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. Puedes ser
if (list.contains(x)) { list.remove(x) }
, solo necesitalist.remove(x)
). Pero, también puede ocultar errores.Si llamar
MediaPlayer.stop()
cuando ya está detenido no tiene ningún daño en su aplicación, entonces dejaría que se ejecute en silencio porque simplifica el código y me encantan los métodos idempotentes. Pero si está absolutamente seguro de queMediaPlayer.stop()
nunca se llamaría en esas condiciones, arrojaría un error porque podría haber un error en otra parte del código y la excepción ayudaría a rastrearlo.fuente
playToggle()
sea más fácil de usar: para lograr el efecto deseado (jugador jugando o no jugando), se le requerirá saber su estado actual. Por lo tanto, si recibe una entrada del usuario que le solicita que detenga el reproductor, primero deberá preguntar si el jugador está jugando actualmente, luego alternar su estado o no, dependiendo de la respuesta. Eso es mucho más engorroso que simplemente llamar a unstop()
método y terminarlo.playToggle
sería muy engorroso de usar. Pero si el usuario también recibió un botón de alternar, entoncesplayToggle
sería más fácil de usar que reproducir / detener.