Manejo de Errores y claridad de codigo

Supongamos que tenemos un procedure que recibe dos parámetros y devuelve otro.

Para hacerlo facil, recibe una moneda y una fecha y devuelve la cotización.

Procedure Cotizacion
(IN:&Moneda, IN:&Fecha, OUT:&Cotizacion)

Puede usarse de la forma:

&CotizacionHoy = Cotizacion('USD',&Today)

Hasta ahi, el codigo es bien entendible. 

Supongamos ahora, que se quiere hacer algun manejo de errores y para lograrlo, se le agrega un parámetro adicional de salida

Procedure Cotizacion
(IN:&Moneda, IN:&Fecha, OUT:&Cotizacion, OUT:&CodigoError)

El procedure ahora ya tiene mas de parámetro de salida, lo cual  hace mas dificil su uso

Cotizacion.call('USD',&Today,&CotizacionHoy,&CodigoError)
If &CodigoError='404'
    Msg('No existe la cotizacion para la moneda en ese dia')
Endif. 
 
Si comparo

&CotizacionHoy = Cotizacion('USD',&Today) 

con

Cotizacion.call('USD',&Today,&CotizacionHoy,&CodigoError)

resulta mucho mas dificil de entender el segundo (al menos para mi). 

Se pueden usar valores especiales para devolver un error, por ejemplo poniendo 

&Cotizacion = -1 

cuando no existe. Esto es peligroso pues alguien puede usar ese valor sin validar y hacer cuentas con eso puede dar resultados erroneos. 

Tambien se pueden usar algun mecanismo de compartir variables globales (por ejemplo, websession, cookies, etc ) para guardar ahi el codigo de error pero es peligroso. 

Resumiendo, no encuentro una forma que el codigo quede entendible y tengamos un buen manejo de errores. 

Si alguien tiene alguna sugerencia o una mejor forma de manejar los errores, estaria bueno que la compartiera. 
 



Comentarios

  1. Yo pondría el parámetro de retrno como último parámetro para poder seguir haciendo una asignación directa.

    parm(in:&moneda, in:&fecha, out:&hasError, out:&cotizacion);

    Y en la llamada sacaría el call, pero es tema de gustos no más:

    &cotizacion = Cotizacion('USD', &Today, &hasError)

    ResponderBorrar
    Respuestas
    1. Si, hcerlo de esa forma es mejor, pero es confuso, pues parece que el parametro &hasError fuera de entrada y en realidad es de salida.

      Borrar
    2. Si, de acuerdo. Pero se puede mitigar con nomenclatura. Prefijando los parámetros o cambiando el casing según corresponda. (hay que tomar con convencion para los inout)

      Borrar
  2. Este comentario ha sido eliminado por el autor.

    ResponderBorrar
    Respuestas
    1. Marcos, invirtiendo los parametros de salida, queda mas claro, pero siempre parece que el parametro del error es de entrada y no de salida.

      Borrar
  3. Creo que inicialmente habría que definir cómo manejar la devolución de erroers para toda la app., de forma tal de que pase a ser un estándar. Si usas un código de error, luego habrá que interpretarlo, lo que hago a menudo es devolver un &Msg con el texto de error (vacío=Funcionó ok) y lo hago así en toda la app. Otra cosa que hago habitualmente es darle algo de semántica al objeto, por ejemplo "DevuelveCotizacion" u "GetCotizacion", así queda un poco más claro el propósito y la lectura.
    Lo que sí es cierto es que mirando el call no surge cuáles son de entrada y cuáles de salida.

    Nota para Artech: Hoy re-escribiendo el primer paréntesis luego del objeto o call, muestra en un mensaje los parámetros, sería bueno que lo haga al posicionarse sobre el nombre del objeto (sin tener que re escribir el paréntesis) y que además muestre si son in, out o ambos.

    ResponderBorrar
    Respuestas
    1. Guillermo:
      De acuerdo con lo que planteas.

      Entiendo que agregas un parametro &MsgError a todos los objetos que puedan dar errores, no ?

      Lo de mostrar si los parametros son IN: OUT: o INOUT: en el intellisense lo habia pedido hace unas versiones atras, pero aun no ha sido implementado.

      Gracias por el aporte.

      Borrar
  4. Hola

    Puedes utilizar sdt, donde crees una estructura que devuelve el valor, y por otro lado el o los mensajes de error

    CotizacionValor
    ErrorSiNo
    {
    CodigoError
    MensajeError
    }

    Donde cambia es en la llamada, que la variable &CotizacionHoy es un sdt

    &CotizacionHoy = Cotizacion('USD',&Today)

    Es lo que utilizo en ciertos casos

    Saludos

    Leonardo Zepeda A.

    ResponderBorrar
    Respuestas
    1. Leonardo:

      Es verdad que con un SDT el codigo queda mas claro, pero se hace bastante tener que crear un SDT adicional para cada variable elemental que tenga que recibir en parametro.

      Como vos decis, es para usar en ciertos casos, pero es dificil de generalizar.

      Gracias por el aporte,
      Enrique

      Borrar
  5. Luego de varias idas y vueltas me resulto muy practico lo que comenta Leonardo Zepeda respecto de armar SDT, mezclado con lo que dice Guillermo Grimalt de definicion en toda la app. De todas maneras, creo que Artech podria incoporar esto de manera nativa, tal como lo hace con las llamadas a los webservices SOAP con el GetSOAPError o mediante un SDT de tipo Messages (coleccion) con el nombre del objeto que se esta llamando. Al regreso de ese objeto solo debemos verificar el Count() y listo.

    ResponderBorrar
    Respuestas
    1. Leandro:
      Lo he pensado un poco, y el tener un GetError podria ser una opcion, pero se me complico el analisis cuando tengo un llamado anidado.

      Por ejemplo, si un Proc1 llama a Proc2 y este llama a Proc3.
      Como se entera Proc1 si hay un error en Proc3?.
      Es responsabilidad de Proc2 avisarle? o solo recupera el error del ultimo llamado?
      El manejo de errores y excepciones no es algo facil. Creo que hay que discutirlo entre todos para llegar a algo mejor que lo que tenemos hoy.

      Gracias por el aporte
      Enrique

      Borrar
  6. Leandro, otra forma de poder manipular esto es con un SDT de tipo collection con una estructura determinada, en la cual podes cargar un tipo de elemento, un comentario y un valor. Por ejemplo lo siguiente

    Collection
    ----Datos
    --------DatosItem
    ------------Tipo
    ------------Mensaje
    ------------Valor

    De esta forma, en el mismo SDT podés devolver todos los resultados que quieras, mantenes la misma llamada del tipo &SdtSalida = Cotizacion(&Moneda, &Fecha), y luego recorres dentro de &SdtSalida.Datos.Items para encontrar el mensaje de error o el valor que te devuelve

    ResponderBorrar
    Respuestas
    1. Ramiro:
      He utilizado esta tecnica, en algunos casos, pero me resulta un poco pesada para usarla en casos sencillos como el que planteaba de la cotizacion.
      Porque si bien el codigo de llamada queda mas claro, luego la recorrida del SDT ensucia un poco el codigo.

      Gracias por el aporte
      Enrique

      Borrar
  7. El manejo de errores es algo que en KB grandes se complica y mas cuando se consumen WS o REST. No se si es lo mas claro pero re-escribí toda una kb y para poder tener un buen manejo de errores todos las llamadas devuelven Codigo de Error y Mensaje.

    Cotizacion.call('USD',&Today,&CotizacionHoy,&CodigoError, &MensajeError)
    if &CodigoError<>0
    //Sigo con el código.
    else
    msg(&MensajeError)
    endif

    No se si es lo mejor pero el manejo de errores tiene que existir y en el pasado por no tener manejo de errores se ocasionaban problemas. Si se les ocurre una mejor estaría bueno ya que es algo fundamental.

    ResponderBorrar
    Respuestas
    1. La que vos pones, es una de las opciones, pero creo que hace que el codigo quede menos claro. Hoy en dia, es de las mejores que tenemos, pero la idea del post era pensar si no habia alguna forma mejor de hacerlo.

      Una opcion, seria la incorporación de Try/Catch en Genexus.
      Eso nos daría la posibilidad de manejar los errores en caso que los hubiera, pero no ensuciar tanto el código.

      Borrar
  8. Creo que la mejor opción es grabar los errores en una variable global, para no andar pasando parametros a lo loco en un call anidado.
    En la master page, consultar esa variable global y si hay errores mostrarlos.
    Por el tema seguridad, vería la posibilidad de encriptar los datos.

    Saludos. Gabriel Fernandez

    ResponderBorrar
    Respuestas

    1. Gabriel:
      Esa es una de las opciones posibles, la cual es buena en caso de una aplicacion WEB y puede funcionar. No sirve (hay que hacerle adaptaciones) por ejemplo para web services, o para aplicaciones mobile.

      Cuando hay que manejar los errores en forma diferente para diferentes ejecutables, la cosa puede complicarse un poco.

      gracias por el comentari.

      Borrar

Publicar un comentario

1) Lee el post
2) Poné tu opinión sobre el mismo.
Todos los comentarios serán leidos y la mayoría son publicados.

Entradas más populares de este blog

Aplicación monolítica o distribuida?

La nefasta influencia del golero de Cacho Bochinche en el fútbol uruguayo

Funcionalidades de GeneXus que vale la pena conocer: DATE Constants.