Cuando se programa por contrato, una función o método primero verifica si se cumplen sus condiciones previas, antes de comenzar a trabajar en sus responsabilidades, ¿verdad? Las dos formas más importantes que hacer estas comprobaciones son por assert
y por exception
.
- afirmar falla solo en modo de depuración. Para asegurarse de que es crucial (unidad) probar todas las condiciones previas del contrato por separado para ver si realmente fallan.
- la excepción falla en el modo de depuración y liberación. Esto tiene el beneficio de que el comportamiento de depuración probado es idéntico al comportamiento de liberación, pero incurre en una penalización de rendimiento en tiempo de ejecución.
¿Cuál crees que es preferible?
Ver pregunta relevante aquí
exception
assert
design-by-contract
andreas buykx
fuente
fuente
Respuestas:
Deshabilitar la afirmación en las versiones de lanzamiento es como decir "Nunca tendré ningún problema en una versión de lanzamiento", lo que a menudo no es el caso. Por lo tanto, afirmar no debe deshabilitarse en una versión de lanzamiento. Pero tampoco quiere que la versión de lanzamiento se bloquee cuando se producen errores, ¿verdad?
Así que usa excepciones y úsalas bien. Use una jerarquía de excepciones buena y sólida y asegúrese de atrapar y puede enganchar la excepción lanzando su depurador para atraparlo, y en el modo de liberación puede compensar el error en lugar de un bloqueo directo. Es el camino más seguro.
fuente
La regla general es que debe usar aserciones cuando intente detectar sus propios errores y excepciones cuando intente detectar los errores de otras personas. En otras palabras, debe usar excepciones para verificar las condiciones previas para las funciones API públicas y cada vez que obtenga datos externos a su sistema. Debe usar afirmaciones para las funciones o datos que son internos de su sistema.
fuente
El principio que sigo es el siguiente: si una situación puede evitarse de manera realista mediante la codificación, utilice una afirmación. De lo contrario, use una excepción.
Las afirmaciones son para garantizar que se cumpla el Contrato. El contrato debe ser justo, de modo que el cliente debe estar en condiciones de garantizar que cumple. Por ejemplo, puede indicar en un contrato que una URL debe ser válida porque las reglas sobre qué es y qué no es una URL válida son conocidas y coherentes.
Las excepciones son para situaciones que están fuera del control tanto del cliente como del servidor. Una excepción significa que algo ha salido mal, y no hay nada que se haya podido hacer para evitarlo. Por ejemplo, la conectividad de red está fuera del control de las aplicaciones, por lo que no se puede hacer nada para evitar un error de red.
Me gustaría agregar que la distinción Afirmación / Excepción no es realmente la mejor manera de pensarlo. En lo que realmente quiere pensar es en el contrato y cómo se puede hacer cumplir. En mi ejemplo de URL anterior, lo mejor que puede hacer es tener una clase que encapsule una URL y sea Null o una URL válida. Es la conversión de una cadena en una URL que hace cumplir el contrato, y se lanza una excepción si no es válida. Un método con un parámetro URL es mucho más claro que un método con un parámetro String y una aserción que especifica una URL.
fuente
Las afirmaciones son para detectar algo que un desarrollador ha hecho mal (no solo usted, sino también otro desarrollador de su equipo). Si es razonable que un error del usuario pueda crear esta condición, entonces debería ser una excepción.
Del mismo modo, piense en las consecuencias. Una afirmación normalmente cierra la aplicación. Si hay alguna expectativa realista de que la condición pueda recuperarse, probablemente debería usar una excepción.
Por otro lado, si el problema solo puede deberse a un error del programador, utilice una afirmación, porque desea saberlo lo antes posible. Una excepción puede ser atrapada y manejada, y nunca la descubrirás. Y sí, debe deshabilitar las afirmaciones en el código de lanzamiento porque allí desea que la aplicación se recupere si existe la más mínima posibilidad. Incluso si el estado de su programa está profundamente roto, el usuario podría guardar su trabajo.
fuente
No es exactamente cierto que "afirmar falla solo en modo de depuración".
En Construcción de software orientado a objetos, segunda edición de Bertrand Meyer, el autor deja una puerta abierta para verificar las condiciones previas en el modo de lanzamiento. En ese caso, lo que sucede cuando falla una aserción es que ... ¡se genera una excepción de violación de aserción! En este caso, no hay recuperación de la situación: sin embargo, se podría hacer algo útil, y es generar automáticamente un informe de error y, en algunos casos, reiniciar la aplicación.
La motivación detrás de esto es que las condiciones previas son generalmente más baratas de probar que las invariantes y las condiciones posteriores, y que en algunos casos la corrección y la "seguridad" en la versión de lanzamiento son más importantes que la velocidad. es decir, para muchas aplicaciones, la velocidad no es un problema, sino la robustez (la capacidad del programa de comportarse de manera segura cuando su comportamiento no es correcto, es decir, cuando se rompe un contrato).
¿Debería dejar siempre habilitados los controles de precondición? Depende. Tu decides. No hay una respuesta universal. Si está creando software para un banco, podría ser mejor interrumpir la ejecución con un mensaje alarmante que transferir $ 1,000,000 en lugar de $ 1,000. Pero, ¿y si estás programando un juego? Tal vez necesite toda la velocidad que pueda obtener, y si alguien obtiene 1000 puntos en lugar de 10 debido a un error que las condiciones previas no detectaron (porque no están habilitadas), mala suerte.
En ambos casos, idealmente debería haber detectado ese error durante la prueba, y debería hacer una parte significativa de su prueba con las aserciones habilitadas. Lo que se está discutiendo aquí es cuál es la mejor política para aquellos casos raros en los que las condiciones previas fallan en el código de producción en un escenario que no se detectó antes debido a pruebas incompletas.
Para resumir, puede tener afirmaciones y aún obtener las excepciones automáticamente , si las deja habilitadas, al menos en Eiffel. Creo que para hacer lo mismo en C ++, debe escribirlo usted mismo.
Ver también: ¿ Cuándo deben permanecer las afirmaciones en el código de producción?
fuente
Hubo un gran hilo con respecto a la habilitación / deshabilitación de aserciones en versiones de compilación en comp.lang.c ++. Moderado, que si tiene algunas semanas puede ver qué tan variadas son las opiniones al respecto. :)
Al contrario de coppro , creo que si no está seguro de que una aserción se pueda deshabilitar en una versión de lanzamiento, entonces no debería haber sido una aserción. Las afirmaciones son para proteger contra la invasión de los invariantes del programa. En tal caso, en lo que respecta al cliente de su código, habrá uno de dos posibles resultados:
No hay diferencia para el usuario, sin embargo, es posible que las afirmaciones agreguen un costo de rendimiento innecesario en el código que está presente en la gran mayoría de las ejecuciones donde el código no falla.
La respuesta a la pregunta en realidad depende mucho más de quiénes serán los clientes de la API. Si está escribiendo una biblioteca que proporciona una API, entonces necesita algún tipo de mecanismo para notificar a sus clientes que han utilizado la API incorrectamente. A menos que proporcione dos versiones de la biblioteca (una con aserciones y otra sin ellas), es muy poco probable que afirmar sea la opción adecuada.
Personalmente, sin embargo, tampoco estoy seguro de ir con excepciones para este caso. Las excepciones se adaptan mejor a dónde puede tener lugar una forma adecuada de recuperación. Por ejemplo, puede ser que esté intentando asignar memoria. Cuando detecta una excepción 'std :: bad_alloc', es posible liberar memoria y volver a intentarlo.
fuente
Describí mi punto de vista sobre el estado del asunto aquí: ¿Cómo se valida el estado interno de un objeto? . En general, haga valer sus reclamos y lance por violación de otros. Para deshabilitar afirmaciones en versiones de lanzamiento, puede hacer:
Por supuesto, en las versiones de lanzamiento, las aserciones fallidas y las excepciones no detectadas deben manejarse de otra manera que no sea en las versiones de depuración (donde simplemente podría llamarse std :: abort). Escriba un registro del error en alguna parte (posiblemente en un archivo), dígale al cliente que ocurrió un error interno. El cliente podrá enviarle el archivo de registro.
fuente
está preguntando sobre la diferencia entre los errores de tiempo de diseño y tiempo de ejecución.
Las afirmaciones son notificaciones de 'oye programador, esto está roto', están ahí para recordarte los errores que no habrías notado cuando ocurrieron.
las excepciones son las notificaciones de 'oye usuario, algo salió mal' (obviamente, puedes codificar para atraparlas para que el usuario nunca sea informado) pero están diseñadas para ocurrir en tiempo de ejecución cuando el usuario de Joe está usando la aplicación.
Entonces, si crees que puedes eliminar todos tus errores, usa solo excepciones. Si crees que no puedes ... usa excepciones. Todavía puede usar afirmaciones de depuración para reducir el número de excepciones, por supuesto.
No olvide que muchas de las condiciones previas serán datos proporcionados por el usuario, por lo que necesitará una buena manera de informar al usuario que sus datos no fueron buenos. Para hacer eso, a menudo necesitará devolver los datos de error por la pila de llamadas a los bits con los que está interactuando. Las afirmaciones no serán útiles entonces, doblemente si su aplicación es n-tier.
Por último, no usaría ninguno: los códigos de error son muy superiores para los errores que cree que ocurrirán regularmente. :)
fuente
Prefiero el segundo. Si bien sus pruebas pueden haber funcionado bien, Murphy dice que algo inesperado saldrá mal. Entonces, en lugar de obtener una excepción en la llamada al método erróneo real, terminas rastreando una NullPointerException (o equivalente) 10 cuadros de pila más profundos.
fuente
Las respuestas anteriores son correctas: use excepciones para las funciones API públicas. El único momento en que puede desear romper esta regla es cuando la verificación es computacionalmente costosa. En ese caso, puedes ponerlo en una afirmación.
Si cree que es probable la violación de esa precondición, manténgala como una excepción o refactorice la precondición.
fuente
Deberías usar ambos. Las afirmaciones son para su conveniencia como desarrollador. Las excepciones detectan cosas que te perdiste o que no esperabas durante el tiempo de ejecución.
Me he aficionado a las funciones de informe de errores de glib en lugar de las simples afirmaciones antiguas. Se comportan como declaraciones de afirmación, pero en lugar de detener el programa, simplemente devuelven un valor y dejan que el programa continúe. Funciona sorprendentemente bien y, como beneficio adicional, puede ver qué sucede con el resto de su programa cuando una función no devuelve "lo que se supone que debe". Si se bloquea, sabe que su comprobación de errores es laxa en otro lugar en el futuro.
En mi último proyecto, utilicé este estilo de funciones para implementar la comprobación de condiciones previas, y si una de ellas fallaba, imprimiría un seguimiento de la pila en el archivo de registro pero seguiría ejecutándose. Me ahorró toneladas de tiempo de depuración cuando otras personas se encontrarían con un problema al ejecutar mi compilación de depuración.
Si necesitara una verificación de argumentos en tiempo de ejecución, haría esto:
fuente
Intenté sintetizar varias de las otras respuestas aquí con mis propios puntos de vista.
Utilice las aserciones para los casos en los que desee deshabilitarlo en producción, por error, para dejarlos dentro. La única razón real para deshabilitar en producción, pero no en desarrollo, es acelerar el programa. En la mayoría de los casos, esta aceleración no será significativa, pero a veces el código es crítico o la prueba es computacionalmente costosa. Si el código es de misión crítica, entonces las excepciones pueden ser mejores a pesar de la desaceleración.
Si hay alguna posibilidad real de recuperación, use una excepción ya que las aserciones no están diseñadas para recuperarse. Por ejemplo, el código rara vez está diseñado para recuperarse de errores de programación, pero está diseñado para recuperarse de factores tales como fallas de red o archivos bloqueados. Los errores no deben manejarse como excepciones simplemente por estar fuera del control del programador. Más bien, la previsibilidad de estos errores, en comparación con los errores de codificación, los hace más amigables para la recuperación.
Repita el argumento de que es más fácil depurar aserciones: el seguimiento de la pila de una excepción con el nombre adecuado es tan fácil de leer como una aserción. Un buen código solo debe capturar tipos específicos de excepciones, por lo que las excepciones no deben pasar desapercibidas debido a que se detectan. Sin embargo, creo que Java a veces te obliga a atrapar todas las excepciones.
fuente
La regla general, para mí, es usar expresiones de aserción para encontrar errores internos y excepciones para errores externos. Puede beneficiarse mucho de la siguiente discusión de Greg desde aquí .
PD: es posible que desee consultar la pregunta similar: Excepción Vs Afirmación .
fuente
Ver también esta pregunta :
fuente