¿Debo hacer mis propias excepciones o cooptar excepciones similares para propósitos ligeramente no estándar?

8

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.

GregRos
fuente
Lo que quieres es reutilizar el nombre pero no el tipo. Hay un significado específico asociado a MissingMemberException. Simplemente defina un nuevo tipo que sea específico para su marco y que tenga exactamente el significado correcto.
usr

Respuestas:

7

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_errorvs. 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.

Niall
fuente
2
Esta es una estrategia brillante cuando se subclasifican las excepciones. No estoy seguro de si estás diciendo que subclase las excepciones en todos los casos o simplemente estás diciendo que cuando subclases así es como debes hacerlo. Con respecto a si debe subclase, estoy de acuerdo con la respuesta publicada por @DanielS.
Chuck Conway
3

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:

  • Escribir mis propias interfaces de contenedor de IoC o de otro modo jugar con Castle.Windsor
  • Determinar cómo voy a manejar la asignación de tipos en la aplicación
  • Escribir lógica de flujo de control para clases base abstractas que se asemejan Interceptors y ajustar llamadas a miembros abstractos con manejo de excepciones
  • Creación de clases base abstractas para IRepository, IUnitOfWork, IDomainService, ICrossCuttingValidation, etc.
  • Intentar describir un modelo de objeto jerárquico para algo cuya naturaleza misma niega tal metaenfoque ( comoIFrameworkException )

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 Exceptioncuando:

  • En realidad, ha experimentado una excepción de tiempo de ejecución que busca manejar en general
  • La excepción de tiempo de ejecución no es algo que pueda descartar razonablemente en tiempo de diseño
  • La excepción aún no está bien cubierta por la gran cantidad existente de Exceptionsubclases
  • De hecho, tiene una idea en mente de cómo desea manejar la excepción O si no, solo porque es tan complicado que necesita pensarlo y volver a hacerlo
  • Su idea va más allá de cambiar sus metadatos, su alcance, su excepción interna, etc., y trata más con el objeto o evento subyacente que produce la excepción y / o los medios para tratarlo realmente
  • Ya ha escrito o no es responsable de la mayoría del lado de entrada de cosas como la interfaz de usuario
tacos_tacos_tacos
fuente
Agradezco tu enfoque pragmático. Como con todo, depende.
Chuck Conway
1

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

DanielS
fuente
Debería ampliar su respuesta un poco y dar un "por qué" a cada punto. Estás en el lugar aquí. Crear excepciones personalizadas es otro sistema / jerarquía que mantener.
Chuck Conway