Esta es una pregunta de diseño general, pero me estoy centrando en C # y .NET porque esos son los lenguajes con los que estoy trabajando en este momento.
¿Debo crear mis propias clases de excepción nuevas o cooptar excepciones de framework existentes para propósitos ligeramente diferentes?
Por ejemplo, me encuentro necesitando una excepción que indica que se esperaba un miembro (es decir, de un tipo), pero que no se ha encontrado al leer un ensamblado usando Mono.Cecil
. La biblioteca de clases base define la excepción MissingMemberException
, que parece comunicar exactamente lo que quiero, pero la descripción dice:
La excepción que se produce cuando se intenta acceder dinámicamente a un miembro de la clase que no existe.
Lo cual no encaja exactamente, porque no estoy accediendo dinámicamente al miembro, sino que estoy trabajando con el ensamblaje pasivamente.
fuente
Respuestas:
En general
Subclasificar las excepciones es una buena idea para agrupar las excepciones en las familias o agrupaciones a las que pertenecen. También le permite al código del cliente la oportunidad de manejar excepciones basadas en errores específicos o de manera más general. Por lo general, divido el árbol en algún lugar cerca de la raíz entre los errores lógicos y de tiempo de ejecución (por ejemplo, en C ++, el
std::runtime_error
vs.std::logic_error
).Poder diferenciar entre las excepciones del sistema y las excepciones de su aplicación también es una buena idea. Sería extraño si el código del cliente está buscando algún tipo de falla del sistema, pero detecta el error lógico del código (a través de una clase base).
Dado este caso y C #
C # tiene una jerarquía de excepciones rica y grande: no intentaría elegir una clase base demasiado profunda. Siguiendo con su ejemplo, parece que está lanzando un error lógico (o al menos un error de aplicación), por lo que la clase base o incluso esa excepción indicaría un error del sistema. La excepción exacta se puede debatir, pero creo que en este caso no es una buena opción.
Jerarquía
Construiría una jerarquía que le permita distinguir entre los errores del sistema y de la aplicación y luego más entre el tiempo de ejecución y los errores lógicos. No tenga miedo de usar excepciones internas: detecte el error del sistema y envuélvalo en algo más significativo dado el contexto en el que se produce. No se exceda, concéntrese en lo que serían excepciones clave. Las excepciones pueden tener descripciones textuales únicas asociadas con ellas; úselas para dar al usuario aún más detalles o pistas sobre qué hacer para corregir el problema.
fuente
La respuesta es, por supuesto, "depende", pero dado su caso de uso, yo diría que no. Este es el por qué:
C # es un maravilloso lenguaje de orientación a objetos. Es tan bueno, de hecho, que a veces me veo atrapado tratando de implementar una visión fanáticamente purista de lo que comenzó como un proyecto simple. Eso no me sucede tanto cuando escribo JavaScript o Objective-C o código PHP.
Cuando entro en este "modo", este tipo de parálisis, uno de los síntomas puede ser una obsesión con el manejo de excepciones y los genéricos y la metaprogramación. A veces los tres juntos. Es mucho más probable que sea un problema cuando estoy trabajando en algo nuevo, relativamente desconocido o con especificaciones y objetivos de mala calidad. En lugar de atascarme en los detalles de implementación , creo, ¿por qué intentar crear algo que pueda acomodar la mayoría de los escenarios que razonablemente anticipo? Luego, cuando llegue a obtener los detalles, se establecerán los cimientos y escribiré un montón de pequeños métodos rechonchos, y así sucesivamente. Tal vez alguien más trabaje en el código de la interfaz de usuario, solo les proporcionaré estos buenos servicios y estos contratos ...
Desafortunadamente, eso aún no me ha sucedido. Lo que suele suceder es que empiezo por este camino trabajando en "infraestructura" - cosas como:
Castle.Windsor
Interceptors
y ajustar llamadas a miembros abstractos con manejo de excepcionesIRepository
,IUnitOfWork
,IDomainService
,ICrossCuttingValidation
, etc.IFrameworkException
)Cuando te obsesiones con respecto a si la descripción de una excepción es lo suficientemente precisa para tus gustos, realmente estás negando que se implemente alguna otra característica crítica. La naturaleza de la excepción también es reveladora: no es una excepción impulsada por eventos , ocurre evidentemente lejos de cualquier metodología de entrada (como una interfaz de usuario o una tubería o algo así) y es probablemente de naturaleza determinista, como en, no puede predecir lo que ingresará un usuario, pero puede predecir qué ensamblaje elige cargar o no.
Por cierto, si la descripción te molesta tanto, ¿no puedes escribir un método de extensión para anularlo en el ámbito apropiado?
Así que, en realidad, soy solo yo proyectando mi propia parálisis de sobre-abstracción y análisis sobre ti, pero creo que puede ser apropiado. Yo diría que para mantenerse en el lado seguro, solo subclase y
Exception
cuando:Exception
subclasesfuente
En mi opinión, las excepciones personalizadas tienen sentido solo en 2 casos:
1) estás desarrollando una API
2) lo usa en su código para recuperarse de una falla.
En cualquier otro escenario, las excepciones integradas deberían ser suficientes.
En su caso, podría usar InvalidArgumentException
fuente