La pregunta fundamental que debe hacerse es: ¿Cuál quiere que sea la interfaz para su función? En particular, ¿es la condición _Materials.ContainsKey(name)
una condición previa de la función?
Si es no una condición previa, la función debe proporcionar un resultado bien definido para todos los posibles valles de name
. En este caso, la excepción lanzada si name
no forma parte de _Materials
la interfaz de la función. Eso significa que debe ser parte de la documentación de la interfaz y si alguna vez decide cambiar esa excepción en el futuro, será un cambio de interfaz de última hora .
La pregunta más interesante es qué sucede si es una condición previa. En ese caso, la condición previa se convierte en parte de la interfaz de la función, pero la forma en que se comporta una función cuando se viola esta condición no es necesariamente parte de la interfaz.
En este caso, el enfoque que publicó, verificando las infracciones de precondición e informando el error, es lo que se conoce como programación defensiva . La programación defensiva es buena porque notifica al usuario temprano cuando cometió un error y llamó a la función con un argumento falso. Es malo, ya que aumenta significativamente la carga de mantenimiento, ya que el código de usuario puede depender de que la función maneje las infracciones de precondición de cierta manera. En particular, si encuentra que la verificación del tiempo de ejecución se convierte en un cuello de botella en el rendimiento en el futuro (poco probable para un caso tan simple, pero bastante común para condiciones previas más complejas), es posible que ya no pueda eliminarlo.
Resulta que esas desventajas pueden ser muy significativas, lo que le dio a la programación defensiva una mala reputación en ciertos círculos. Sin embargo, el objetivo inicial sigue siendo válido: queremos que los usuarios de nuestra función se den cuenta temprano cuando cometieron un error.
Por lo tanto, muchos desarrolladores hoy en día proponen un enfoque ligeramente diferente para este tipo de problemas. En lugar de lanzar una excepción, utilizan un mecanismo de aseveración para verificar las condiciones previas. Es decir, las condiciones previas pueden verificarse en las compilaciones de depuración para ayudar a los usuarios a detectar errores desde el principio, pero no son parte de la interfaz de funciones. La diferencia puede parecer sutil a primera vista, pero puede hacer una gran diferencia en la práctica.
Técnicamente, llamar a una función con condiciones previas violadas es un comportamiento indefinido. Pero la implementación podría decidir detectar esos casos y notificar al usuario de inmediato si eso sucede. Desafortunadamente, las excepciones no son una buena herramienta para implementar esto, ya que el código de usuario puede reaccionar ante ellas y, por lo tanto, podría comenzar a depender de su presencia.
Para obtener una explicación detallada de los problemas con el enfoque defensivo clásico, así como una posible implementación para verificaciones de precondición de estilo asertivo, consulte la charla de John Lakos Programación defensiva realizada directamente desde CppCon 2014 ( Diapositivas , video ).
_Materials.Add
Lanza una excepción?