¿Lanzas una excepción de argumento o una excepción de argumento de métodos privados?
20
Estaba revisando un código que escribí hace un tiempo, y puedo ver que tengo un par de métodos privados que arrojan argumentos y / o excepciones argumentativas si hay problemas con los parámetros de los métodos.
Supongo que mi razonamiento es que ayuda a probar la aplicación en el futuro si alguien intenta "mal usar" el método en el futuro. Sin embargo, dado que es un método privado y las personas que pueden llamar a este método pueden ver los comentarios y el código asociados, es innecesario lanzar esto. Ciertamente no hace daño tenerlos, aunque agrega desorden.
Creo que estas excepciones son generalmente más útiles en algo como una API que se expondrá públicamente.
Normalmente, para los métodos privados no arroja excepciones, ya que tal como lo escribió, se supone que el desarrollador debe saber cómo y desde dónde está llamando el método. Como tal, las variables pasadas como parámetros al método privado deben verificarse fuera del método, es decir, antes de llamarlo. Lanzar "InvalidArgumentException" y otras excepciones se considera una buena práctica para los métodos públicos (ya sea que esté escribiendo una "API" o no).
Para aquellos casos en los que desea lanzar "InvalidArgumentException", vale la pena mencionar que hay una Assertclase en Spring API para Java desde la versión 1.1.2. Ha sido muy útil, al menos para mí, escribir menos código para realizar verificaciones.
Sin embargo, puede usar "afirma" para verificar los parámetros en métodos privados. Ese es uno de sus verdaderos propósitos. Son más razones para usarlos, consulte el siguiente enlace que también explica a fondo cuándo usar afirmaciones y cuándo usar excepciones. Las afirmaciones no deben incluirse en el código de producción y el compilador las elimina de forma predeterminada. Entonces son lo que estás buscando: ayudar a los desarrolladores, invisible para los usuarios. En Java, debe usar un indicador especial ("-ea") para indicarle al compilador que habilite las aserciones. Puede considerarlos como amigos "depuradores".
Apache Commons también tiene una clase Validate que funciona de manera similar a la clase Assert de Spring
Rosa Richter
@cantido sí y también agregaría que la clase Preconditions en Google's Guava funciona de la misma manera. Gracias por la información :-)
Jalayn
2
Como todo lo demás, depende ...
Si los métodos públicos son envoltorios simples que llaman al método privado (en la línea de un método privado sobrecargado), entonces puede tener sentido lanzar una excepción en el método privado en lugar de marcar cada uno público.
En general, si no cumple con la definición anterior, normalmente no comprobaría los argumentos / lanzaría una excepción en un método privado. Aunque hay otros casos, generalmente hago esto en un método privado antes de realizar una operación costosa que podría fallar en parte si los argumentos no son válidos.
Me doy cuenta de que si bien la pregunta no tiene una etiqueta de idioma, probablemente esté hablando implícitamente de "idiomas de café". Pero solo en aras de la exhaustividad, me gustaría mencionar el aparente consenso algo divergente en el mundo de C ++.
Hay tres cosas en las que los programadores de C ++ generalmente estarán interesados:
¿Tendrá cero gastos generales en compilaciones optimizadas? (Es decir, ¿se puede "compilar"?)
¿Puedo usarlo para atrapar a un depurador justo en el punto donde se detectó el error?
¿Puedo usarlo para informar problemas de funciones declaradas noexcept?
En el pasado, me acerqué al primer problema escribiendo código como este
int
factorial(constint n){if(CHECK_ARGS){if(n <0)throw std::invalid_argument {"n < 0"};}int fac =1;for(int i =2; i <= n;++i)
fac *= i;return fac;}
donde CHECK_ARGSestá #defined una constante de tiempo de compilación para que el compilador pueda eliminar completamente todo el código de comprobación de argumentos en compilaciones optimizadas. (No digo que compilar los cheques sea algo bueno en general, pero creo que un usuario debería tener la opción de compilarlos).
Todavía me gusta de esta solución que el código de comprobación de argumentos sea claramente visible agrupado en el if. Sin embargo, el segundo y el tercer problema no se resuelven con esto. Por lo tanto, ahora me estoy inclinando nuevamente hacia el uso de una assertmacro para la verificación de argumentos.
Como desarrollador, si violé una condición previa de una biblioteca que estoy usando, no quiero que la pila se desenrolle. Lo que quiero es un volcado de núcleo o su equivalente, una forma de inspeccionar el estado del programa en el punto exacto donde se detectó el problema. Eso generalmente significa assert()o algo así.
Hubo una charla muy interesante dada por John Lakos en CppCon'14 titulada Programación defensiva bien hecha ( parte 1 , parte 2 ). En la primera parte de su charla, analiza la teoría de los contratos y el comportamiento indefinido. En la segunda parte, presenta lo que considero una muy buena propuesta para la verificación sistemática de argumentos. En esencia, propone macros de afirmación que permiten al usuario seleccionar cuánto presupuesto (en términos de utilización de la CPU) está dispuesto a donar a la biblioteca para la verificación de argumentos y hace que la biblioteca haga un uso racional de ese presupuesto. Además, el usuario también puede instalar una función global de manejo de errores que se llamará en caso de que se detecte un contrato roto.
Con respecto al aspecto de que una función es privada, no creo que esto signifique que nunca deberíamos hacer que verifique sus argumentos. Podemos confiar más en nuestro propio código para no violar el contrato de una función interna, pero también sabemos que tampoco somos perfectos. La comprobación de argumentos en las funciones internas es tan útil para detectar nuestros propios errores como lo es en las funciones públicas para detectar errores en el código del cliente.
Lógica interna: se supone que esta función se llama con los parámetros correctos y, por lo tanto, utiliza afirmaciones para verificar condiciones previas, condiciones posteriores e invariantes para verificar su lógica interna.
Envoltorio de interfaz de usuario: Esta función ajusta la función interna y utiliza InvalidArgumentExceptions para manejar valores erróneos y de avisar al usuario de corregir sus entradas: Assert(x).hasLength(4);, Assume(y).isAlphanumeric();, Assert(z).isZipCode();, Assume(mailAdress).matchesRegex(regex_MailAdress);, Reject(x).ifEmpty();, etc.
Contenedor de interfaz por lotes: esta función envuelve la función interna y utiliza el registro, las marcas de validez y las estadísticas para manejar valores incorrectos sin interrumpir alguna tarea de larga duración. Las marcas podrían ser utilizadas más tarde por alguien que verifique y limpie la base de datos de resultados.
Contenedor de interfaz de línea de comando: esta función envuelve la función interna y vuelve a solicitar la última entrada.
Debe usar ambos, afirmaciones y excepciones, en diferentes métodos para diferentes tareas. Debe separar la lógica interna de la comprobación de parámetros. Compárelo con la separación de Modelo, Vista, Controlador.
Hay mejores formas de evitar la verificación de referencia nula: use el contrato de código o un marco de AOP para hacer la verificación por usted. Google "contrato de código c #" o "postsharp".
Supongo que mi pregunta se extendería a los contratos de código también. ¿Es necesario verificar las condiciones previas del método en un método privado (es decir, para futuras pruebas o para evitar que te dispares en el pie)?
Como todo lo demás, depende ...
Si los métodos públicos son envoltorios simples que llaman al método privado (en la línea de un método privado sobrecargado), entonces puede tener sentido lanzar una excepción en el método privado en lugar de marcar cada uno público.
En general, si no cumple con la definición anterior, normalmente no comprobaría los argumentos / lanzaría una excepción en un método privado. Aunque hay otros casos, generalmente hago esto en un método privado antes de realizar una operación costosa que podría fallar en parte si los argumentos no son válidos.
fuente
Me doy cuenta de que si bien la pregunta no tiene una etiqueta de idioma, probablemente esté hablando implícitamente de "idiomas de café". Pero solo en aras de la exhaustividad, me gustaría mencionar el aparente consenso algo divergente en el mundo de C ++.
Hay tres cosas en las que los programadores de C ++ generalmente estarán interesados:
noexcept
?En el pasado, me acerqué al primer problema escribiendo código como este
donde
CHECK_ARGS
está#define
d una constante de tiempo de compilación para que el compilador pueda eliminar completamente todo el código de comprobación de argumentos en compilaciones optimizadas. (No digo que compilar los cheques sea algo bueno en general, pero creo que un usuario debería tener la opción de compilarlos).Todavía me gusta de esta solución que el código de comprobación de argumentos sea claramente visible agrupado en el
if
. Sin embargo, el segundo y el tercer problema no se resuelven con esto. Por lo tanto, ahora me estoy inclinando nuevamente hacia el uso de unaassert
macro para la verificación de argumentos.Los estándares de codificación de Boost están de acuerdo con esto:
Hubo una charla muy interesante dada por John Lakos en CppCon'14 titulada Programación defensiva bien hecha ( parte 1 , parte 2 ). En la primera parte de su charla, analiza la teoría de los contratos y el comportamiento indefinido. En la segunda parte, presenta lo que considero una muy buena propuesta para la verificación sistemática de argumentos. En esencia, propone macros de afirmación que permiten al usuario seleccionar cuánto presupuesto (en términos de utilización de la CPU) está dispuesto a donar a la biblioteca para la verificación de argumentos y hace que la biblioteca haga un uso racional de ese presupuesto. Además, el usuario también puede instalar una función global de manejo de errores que se llamará en caso de que se detecte un contrato roto.
Con respecto al aspecto de que una función es privada, no creo que esto signifique que nunca deberíamos hacer que verifique sus argumentos. Podemos confiar más en nuestro propio código para no violar el contrato de una función interna, pero también sabemos que tampoco somos perfectos. La comprobación de argumentos en las funciones internas es tan útil para detectar nuestros propios errores como lo es en las funciones públicas para detectar errores en el código del cliente.
fuente
Considere la siguiente estructura:
Lógica interna: se supone que esta función se llama con los parámetros correctos y, por lo tanto, utiliza afirmaciones para verificar condiciones previas, condiciones posteriores e invariantes para verificar su lógica interna.
Envoltorio de interfaz de usuario: Esta función ajusta la función interna y utiliza InvalidArgumentExceptions para manejar valores erróneos y de avisar al usuario de corregir sus entradas:
Assert(x).hasLength(4);
,Assume(y).isAlphanumeric();
,Assert(z).isZipCode();
,Assume(mailAdress).matchesRegex(regex_MailAdress);
,Reject(x).ifEmpty();
, etc.Contenedor de interfaz por lotes: esta función envuelve la función interna y utiliza el registro, las marcas de validez y las estadísticas para manejar valores incorrectos sin interrumpir alguna tarea de larga duración. Las marcas podrían ser utilizadas más tarde por alguien que verifique y limpie la base de datos de resultados.
Contenedor de interfaz de línea de comando: esta función envuelve la función interna y vuelve a solicitar la última entrada.
Debe usar ambos, afirmaciones y excepciones, en diferentes métodos para diferentes tareas. Debe separar la lógica interna de la comprobación de parámetros. Compárelo con la separación de Modelo, Vista, Controlador.
fuente
Hay mejores formas de evitar la verificación de referencia nula: use el contrato de código o un marco de AOP para hacer la verificación por usted. Google "contrato de código c #" o "postsharp".
fuente