¿Cómo debo manejar una entrada de usuario no válida?

12

He estado pensando en este tema por un tiempo y me gustaría saber las opiniones de otros desarrolladores.

Tiendo a tener un estilo de programación muy defensivo. Mi bloque o método típico se ve así:

T foo(par1, par2, par3, ...)
{
    // Check that all parameters are correct, return undefined (null)
    // or throw exception if this is not the case.

    // Compute and (possibly) return result.
}

También, durante el cálculo, verifico todos los punteros antes de desreferirlos. Mi idea es que, si hay algún error y algún puntero NULO debería aparecer en alguna parte, mi programa debería manejar esto bien y simplemente negarse a continuar con el cálculo. Por supuesto, puede notificar el problema con un mensaje de error en el registro o algún otro mecanismo.

Para ponerlo de una manera más abstracta, mi enfoque es

if all input is OK --> compute result
else               --> do not compute result, notify problem

Otros desarrolladores, entre ellos algunos colegas míos, usan otra estrategia. Por ejemplo, no verifican los punteros. Suponen que a un fragmento de código se le debe dar una entrada correcta y no debe ser responsable de lo que suceda si la entrada es incorrecta. Además, si una excepción de puntero NULL bloquea el programa, se encontrará un error más fácilmente durante la prueba y tendrá más posibilidades de ser reparado.

Mi respuesta a esto es normalmente: pero ¿qué sucede si no se encuentra el error durante la prueba y aparece cuando el cliente ya está utilizando el producto? ¿Cuál es una forma preferida para que el error se manifieste? ¿Debería ser un programa que no realiza una determinada acción, pero que aún puede seguir funcionando, o un programa que falla y necesita reiniciarse?

Summarizing

¿Cuál de los dos enfoques para manejar la entrada incorrecta recomendaría?

Inconsistent input --> no action + notification

o

Inconsistent input --> undefined behaviour or crash

Editar

Gracias por las respuestas y sugerencias. Soy un fanático del diseño por contrato también. Pero incluso si confío en que la persona que ha escrito el código llamando a mis métodos (quizás sea yo mismo), todavía puede haber errores, lo que lleva a una entrada incorrecta. Por lo tanto, mi enfoque es nunca asumir que un método se pasa correctamente.

También, usaría un mecanismo para detectar el problema y notificarlo. En un sistema de desarrollo, por ejemplo, sería Abre un diálogo para notificar al usuario. En un sistema de producción, simplemente escribiría alguna información en el registro. No creo que los controles adicionales puedan llevar a problemas de rendimiento. No estoy seguro de si las afirmaciones son suficientes, si están desactivadas en un sistema de producción: tal vez ocurra alguna situación en la producción que no haya ocurrido durante las pruebas.

De todos modos, me sorprendió mucho que muchas personas sigan el enfoque opuesto: dejan que la aplicación se bloquee "a propósito" porque sostienen que esto facilitará la búsqueda de errores durante las pruebas.

    
pregunta Giorgio 25.08.2011 - 00:08

6 respuestas

8

Lo has hecho bien. Se paranoico. No confíe en otro código, incluso si es su propio código. Te olvidas de las cosas, haces cambios, el código evoluciona. No confíes en el código externo.

Se hizo un buen punto arriba: ¿qué sucede si las entradas no son válidas pero el programa no falla? Entonces obtienes basura en la base de datos y errores en la línea.

Cuando se me pide un número (por ejemplo, el precio en dólares o la cantidad de unidades), me gusta ingresar "1e9" y ver qué hace el código. Puede suceder.

Hace cuatro décadas, obteniendo mi B.S. en Informática de U.C.Berkeley, nos dijeron que un buen programa es un 50% de manejo de errores. Sé paranoico.

    
respondido por el Andy Canfield 25.08.2011 - 09:55
7

Ya tienes la idea correcta

¿Cuál de los dos enfoques para manejar la entrada incorrecta recomendaría?

  

Entrada inconsistente - > ninguna acción + notificación

o mejor

  

Entrada inconsistente - > acción adecuadamente manejada

Realmente no puede adoptar un enfoque de programación de cookies (podría), pero terminaría con un diseño de fórmula que hace las cosas por costumbre y no por elección consciente.

Templar el dogmatismo con el pragmatismo.

Steve McConnell lo dijo mejor

Steve McConnell prácticamente escribió el libro ( Code Complete ) sobre programación defensiva y este fue uno de los métodos. que aconsejó que siempre debe validar sus entradas.

No recuerdo si Steve mencionó esto, sin embargo, debe considerar hacerlo para los métodos y funciones no privados , y solo para otros cuando se considere necesario.

    
respondido por el Justin Shield 25.08.2011 - 00:59
3

No hay una respuesta "correcta" aquí, en particular sin especificar el idioma, el tipo de código y el tipo de producto en el que podría entrar el código. Considera:

  • El lenguaje importa. En Objective-C, a menudo está bien enviar mensajes a nil; no pasa nada, pero el programa tampoco falla. Java no tiene punteros explícitos, por lo que los punteros nulos no son una gran preocupación allí. En C, debes ser un poco más cuidadoso.

  • Ser paranoico es una sospecha o desconfianza irrazonable e injustificada. Probablemente no sea mejor para el software que para la gente.

  • Su nivel de preocupación debe ser acorde con el nivel de riesgo en el código y la probable dificultad de identificar cualquier problema que aparezca. ¿Qué pasa en el peor de los casos? ¿El usuario reinicia el programa y continúa donde lo dejó? ¿La compañía pierde millones de dólares?

  • No siempre se puede identificar una entrada incorrecta. Puedes comparar religiosamente tus punteros a cero, pero eso solo captura uno de 2 ^ 32 valores posibles, casi todos de los cuales son malos.

  • Hay muchos mecanismos diferentes para tratar los errores. De nuevo, depende en cierta medida del lenguaje. Puede usar afirmar macros, sentencias condicionales, pruebas unitarias, manejo de excepciones, diseño cuidadoso y otras técnicas. Ninguno de ellos es infalible y ninguno es apropiado para cada situación.

Por lo tanto, en su mayoría se reduce a donde quieres poner la responsabilidad. Si está escribiendo una biblioteca para que otros la usen, es probable que desee ser tan cuidadoso como sea razonable con respecto a las entradas que obtiene, y hacer todo lo posible para emitir errores útiles cuando sea posible. En sus propias funciones y métodos privados, puede usar aserciones para detectar errores tontos, pero de otro modo responsabilizar a la persona que llama (que es usted) para que no pase basura.

    
respondido por el Caleb 25.08.2011 - 09:02
1

Definitivamente debería haber una notificación, como una excepción lanzada. Sirve para avisar a otros programadores que pueden estar haciendo un mal uso del código que usted escribió (tratando de usarlo para algo que no tenía la intención de hacer) de que su entrada no es válida o da como resultado errores. Esto es muy útil para rastrear errores, mientras que si simplemente devuelve un valor nulo, su código continuará hasta que intenten usar el resultado y obtengan una excepción de un código diferente.

Si su código encuentra un error durante una llamada a otro código (tal vez una actualización fallida de la base de datos) que está más allá del alcance de ese fragmento de código en particular, realmente no tiene control sobre él y su único recurso es lanzar un excepción que explica lo que sabe (solo lo que le dice el código que ha llamado). Si sabe que ciertas entradas inevitablemente conducirán a tal resultado, simplemente no puede molestar en ejecutar su código y lanzar una excepción que indique qué entrada no es válida y por qué.

En una nota más relacionada con el usuario final, es mejor devolver algo descriptivo pero simple para que cualquiera pueda entenderlo. Si su cliente llama y dice "el programa se bloqueó, repárelo", tiene mucho trabajo en sus manos para detectar qué fue lo que salió mal y por qué, y con la esperanza de que pueda reproducir el problema. El uso adecuado de las excepciones no solo puede evitar un bloqueo, sino que también proporciona información valiosa. Una llamada de un cliente que dice "El programa me está dando un error. Dice que 'XYZ no es una entrada válida para el método M, porque Z es demasiado grande", o algo así, incluso si no tienen idea de lo que significa, saber exactamente dónde buscar Además, dependiendo de sus prácticas comerciales o las de su empresa, es posible que no sea usted quien solucione estos problemas, por lo que es mejor dejarles un buen mapa.

Entonces, la versión corta de mi respuesta es que tu primera opción es la mejor.

Inconsistent input -> no action + notify caller
    
respondido por el yoozer8 25.08.2011 - 01:18
1

Luché con este mismo problema mientras asistía a una clase universitaria en programación. Me incliné hacia el lado paranoico y tiendo a revisar todo, pero me dijeron que se trataba de un comportamiento equivocado.

Nos estaban enseñando "Diseño por contrato". El énfasis es que las condiciones previas, invariantes y condiciones posteriores se especifiquen en los comentarios y documentos de diseño. Como la persona que implementa mi parte del código, debería confiar en el arquitecto de software y autorizarlo siguiendo las especificaciones que incluirían las condiciones previas (qué entradas deben poder manejar mis métodos y qué entradas no se enviarán) . El control excesivo en cada método llama a la hinchazón.

Las aserciones deben usarse durante las iteraciones de compilación para verificar la corrección del programa (validación de condiciones previas, invariantes, condiciones de publicación). Las aserciones se activarán en la compilación de producción.

    
respondido por el Richard 25.08.2011 - 08:15
0

El uso de "aserciones" es el camino a seguir para notificar a otros desarrolladores que lo están haciendo mal, solo en los métodos "privados", por supuesto . Habilitarlos / deshabilitarlos es solo un indicador para agregar / eliminar en el momento de la compilación y, como tal, es fácil eliminar aserciones del código de producción. También hay una gran herramienta para saber si usted de alguna manera lo está haciendo mal con sus propios métodos.

En cuanto a la verificación de los parámetros de entrada dentro de los métodos públicos / protegidos, prefiero trabajar a la defensiva y verificar los parámetros y lanzar InvalidArgumentException o similares. Es por eso que hay aquí para. También depende de si estás escribiendo una API o no. Si se trata de una API, e incluso más si es de código cerrado, conviene validar todo para que los desarrolladores sepan exactamente qué fue lo que salió mal. De lo contrario, si la fuente está disponible para otros desarrolladores, no es blanco / negro. Solo sé coherente con tus elecciones.

Editar: solo para agregar que si busca por ejemplo en el JDK de Oracle, verá que nunca verifican "nulo" y dejan que el código se bloquee. Ya que va a lanzar una NullPointerException de todos modos, ¿por qué molestarse en verificar si es nulo y lanzar una excepción explícita? Supongo que tiene algún sentido.

    
respondido por el Jalayn 25.08.2011 - 07:43

Lea otras preguntas en las etiquetas