Pregunta

Conozco dos enfoques para el manejo de excepciones, echémosle un vistazo.

  1. Enfoque contractual.

    Cuando un método no hace lo que dice que hará en el encabezado del método, generará una excepción.Por lo tanto, el método "promete" que realizará la operación y, si falla por algún motivo, generará una excepción.

  2. Enfoque excepcional.

    Solo lanza excepciones cuando suceda algo realmente extraño.No debe utilizar excepciones cuando pueda resolver la situación con el flujo de control normal (declaraciones If).No utiliza Excepciones para controlar el flujo, como podría hacerlo en el enfoque de contrato.

Usemos ambos enfoques en diferentes casos:

Tenemos una clase Cliente que tiene un método llamado OrderProduct.

enfoque contractual:

class Customer
{
     public void OrderProduct(Product product)
     {
           if((m_credit - product.Price) < 0)
                  throw new NoCreditException("Not enough credit!");
           // do stuff 
     }
}

enfoque excepcional:

class Customer
{
     public bool OrderProduct(Product product)
     {
          if((m_credit - product.Price) < 0)
                   return false;
          // do stuff
          return true;
     }
}

if !(customer.OrderProduct(product))
            Console.WriteLine("Not enough credit!");
else
   // go on with your life

Aquí prefiero el enfoque excepcional, ya que no es verdaderamente excepcional que un cliente no tenga dinero suponiendo que no haya ganado la lotería.

Pero he aquí una situación en la que me equivoco en cuanto al estilo del contrato.

Excepcional:

class CarController
{
     // returns null if car creation failed.
     public Car CreateCar(string model)
     {
         // something went wrong, wrong model
         return null;
     }
 }

Cuando llamo a un método llamado CreateCar, espero una instancia de Car en lugar de algún pésimo puntero nulo, que puede arruinar mi código en ejecución una docena de líneas más tarde.Por eso prefiero el contrato a este:

class CarController
{

     public Car CreateCar(string model)
     {
         // something went wrong, wrong model
         throw new CarModelNotKnownException("Model unkown");

         return new Car();
     }
 }

¿Cuál estilo usas?¿Cuál cree que es el mejor enfoque general para las excepciones?

¿Fue útil?

Solución

Estoy a favor de lo que usted llama el enfoque de "contrato".No es necesario devolver valores nulos u otros valores especiales para indicar errores en un lenguaje que admita excepciones.Me resulta mucho más fácil entender el código cuando no tiene un montón de cláusulas "if (resultado == NULL)" o "if (resultado == -1)" mezcladas con lo que podría ser una lógica muy simple y directa.

Otros consejos

Mi enfoque habitual es utilizar el contrato para manejar cualquier tipo de error debido a la invocación del "cliente", es decir, debido a un error externo (es decir, ArgumentNullException).

No se manejan todos los errores en los argumentos.Se plantea una excepción y el "cliente" se encarga de manejarla.Por otro lado, para los errores internos, siempre intente corregirlos (como si no pudiera obtener una conexión a la base de datos por algún motivo) y solo si no puede manejarlo, vuelva a generar la excepción.

Es importante tener en cuenta que la mayoría de las excepciones no controladas en ese nivel no podrán ser manejadas por el cliente de todos modos, por lo que probablemente subirán al controlador de excepciones más general, por lo que si ocurre tal excepción, probablemente sea FUBAR de todos modos.

Creo que si está creando una clase que será utilizada por un programa externo (o será reutilizada por otros programas), entonces debería utilizar el enfoque de contrato.Un buen ejemplo de esto es una API de cualquier tipo.

Si realmente está interesado en las excepciones y quiere pensar en cómo usarlas para construir sistemas robustos, considere leer Hacer sistemas distribuidos confiables en presencia de errores de software..

Ambos enfoques son correctos.Lo que eso significa es que un contrato debe redactarse de tal manera que especifique para todos los casos que no sean verdaderamente excepcionales un comportamiento que no requiera lanzar una excepción.

Tenga en cuenta que algunas situaciones pueden o no ser excepcionales según lo que espera la persona que llama el código.Si la persona que llama espera que un diccionario contenga un determinado elemento, y la ausencia de ese elemento indicaría un problema grave, entonces no encontrar el elemento es una condición excepcional y debería provocar que se genere una excepción.Sin embargo, si la persona que llama no sabe realmente si un elemento existe y está igualmente preparada para manejar su presencia o ausencia, entonces la ausencia del elemento sería una condición esperada y no debería causar una excepción.La mejor manera de manejar tales variaciones en las expectativas de la persona que llama es hacer que un contrato especifique dos métodos:un método DoSomething y un método TryDoSomething, p.e.

TValue GetValue(TKey Key);
bool TryGetValue(TKey Key, ref TValue value);

Tenga en cuenta que, si bien el patrón estándar de 'prueba' es como se ilustra arriba, algunas alternativas también pueden ser útiles si uno está diseñando una interfaz que produce elementos:

 // In case of failure, set ok false and return default<TValue>.
TValue TryGetResult(ref bool ok, TParam param);
// In case of failure, indicate particular problem in GetKeyErrorInfo
// and return default<TValue>.
TValue TryGetResult(ref GetKeyErrorInfo errorInfo, ref TParam param);

Tenga en cuenta que usar algo como el patrón TryGetResult normal dentro de una interfaz hará que la interfaz sea invariante con respecto al tipo de resultado;El uso de uno de los patrones anteriores permitirá que la interfaz sea covariante con respecto al tipo de resultado.Además, permitirá que el resultado se utilice en una declaración 'var':

  var myThingResult = myThing.TryGetSomeValue(ref ok, whatever);
  if (ok) { do_whatever }

No es exactamente el enfoque estándar, pero en algunos casos las ventajas pueden justificarlo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top