martes, 10 de febrero de 2009

Mejorando la legibilidad del código

En GeneXus, cuando invocamos un objeto tenemos la posibilidad de hacerlo con un call o con un udp.

Supongamos que tenemos un procedimiento que realiza alguna operación y devuelve un valor. Solo para el ejemplo, supongamos que tenemos un procedimiento Suma que recibe dos valores, &a y &b, y devuelve su suma.

¿Cuál es la diferencia entre invocar dicho procedimiento con call o con udp? Las invocaciones serían así
call(PSuma, &a, &b, &result)
o
&result = udp(PSuma, &a, &b)
¿Cuál de las dos es mejor?

Desde el punto de vista del código generado, son casi idénticos. En realidad no tiene por qué haber diferencia, porque el resultado debe ser exactamente el mismo.

Pero desde el punto de vista de la legibilidad del código, el udp es mucho mejor que el call, porque nos dice cual fue la intensión de la invocación: cargar la variable &result con lo que devuelve el procedimiento.

Citando el blog Coding Horror (que a su vez cita una frase de un libro):
Programs must be written for people to read, and only incidentally for machines to execute.
PD: la palabra legibilidad existe, me quedaban dudas por eso la busqué en el diccionario de la Real Academia Española...

17 comentarios:

  1. La mejor es

    &Result=pSuma(&a,&b) o

    &Result = pSuma.udp(&a,&b)

    Al menos, es la que a mi me queda mas clara..

    ResponderEliminar
  2. &result = PSuma(&a, &b)

    definitivamente es lo mejor del mundo.

    Hace un tiempo que erradicamos el Call o el Udp de nuestro codigo.

    :)

    ResponderEliminar
  3. La pregunta es... ¿Para quien se escribe el código?

    La respuesta obvia es para Genexus quien es en realidad el que debe interpretar la intención del programador sin ambigüedad alguna. En este sentido las variantes son irrelevantes.

    Pero hay sutilezas... Al existir múltiples formas de escribir el mismo código no es suficiente el criterio de la ambigüedad... Debemos aportar criterios extras...

    Coincido con la idea de poner en segundo lugar la facilidad de lectura... Pero este es un criterio subjetivo, podemos hacer un mejor intento.

    Creo que facilita la legibilidad

    Eliminar ruido
    Sacar los elementos que no contribuyen a realizar la tarea. Poner CALL o UDP en este contexto es innecesario y deberían ser eliminados.
    >>&Resultado = udp(PSuma,&a,&b)
    <<&Resultado=PSuma (&a,&b)

    Facilitar el reconocimiento.
    Los elementos más importantes deben ser identificados rápidamente... Es este sentido la 'p' en minúscula me ayuda a centrarme en el nombre de la función. Como así también los espacios en blanco (en este caso rodeando al '=' y después de ','.
    >>&Resultado=PSuma (&a,&b)
    <<&Resultado = pSuma (&a, &b)

    Significado explícito
    Al escribir transformamos una intención en instrucciones, al leer el proceso es el inverso. Continumente estamos ‘semantizando’ el codigo, tratando de entender lo que hace. Poner nombres completos y descriptivos es esencial.
    >>&Resultado = pSuma(&a, &b)
    <<&Final = pCalcularSaldo(&Inicial, &Movimiento)

    Regularidad en el uso.
    Si usamos las mismas formas una y otra vez automatizamos mentalmente los patrones. En ese sentido prefiero las variables siempre en minúsculas, las funciones siempre en mayúsculas, las palabras separadas por guion bajo, etc.
    >>&Final = pCalcularSaldo(&Inicial, &Movimiento)
    <<&final = pCalcular_Saldo(&inicial, &movimiento)


    Creo que me fanatize un poco... ;)

    ResponderEliminar
  4. Hay veces que no es lo mismo, las diferencias se me han dado en el orden de ejecución de las reglas… pero bueno yo sigo en GX9 y por ahí eso ya no es problema en la X… jeje

    En cuanto no usar más el call o el udp… a mí me gusta el colorcito verde del call, como que me ayuda a leer… mucha letra sobre fondo blanco me cuesta un poco más… debe ser una tara mía obviamente.

    Para esta función en particular el udp lo usaría porque leo que es una función que recibe dos parámetros para devolver un resultado, no pienso que me va a devolver algo útil en el parámetro &a… así que voy por el uso del udp.

    ResponderEliminar
  5. Enrique, sauron: Esa forma de invocar al objeto es de GeneXus X, ¿no?. Yo estoy con GX 9 y no la tenía en el radar, pero ahora la probé y no funciona. Al salvar da un warning, al especificar da otro y al compilar da un error...

    ResponderEliminar
  6. Alejandro: Yo también pensaba que escribía el código para el generador o compilador, hasta que leí la frase que puse más arriba.

    En realidad, no es cierto que tengamos que escribir el código para que lo interprete el compilador. El que se va a tener que enfrentar con el código que escribimos es otro programador. El compilador sabe lo que hace, mientras el código sea sintáctica y semánticamente correcto, él se sabe manejar.

    Sin duda no es algo inmediato, pero vale la pena la reflexión.

    Con respecto a los otros puntos, lo de la P en minúscula o el guión bajo en los nombres es discutible. Pero sin duda lo que es necesario es tener normas de nomenclatura consistentes y que ayuden a entender el código.

    Por ejemplo, nosotros nombramos todos los procedimientos cuyo objetivo es devolver un valor con una F adelante. Entonces si veo un código que dice "call(PF...)", ya se que va a devolver algo, aunque no sea un udp. Además se que no va a estar cambiando nada en la base de datos (siempre que se haya usado bien la norma).

    ResponderEliminar
  7. Andrés: Así que vos fuiste el que puso los call en lugar de los udp... Gracias! ;)

    Si estás con GeneXus 9 y el problema es el color, te cuento que en el directorio de instalación de GX hay un archivo Keycolor.ini que tiene la definición de los colores.

    Por defecto el udp tiene "UDP=FUN", y el call tiene "Call=RUL". Supongo que si ponés "UDP=RUL", te lo debería pintar de verde...

    ResponderEliminar
  8. A mi también me gusta usar el udp en todos lados donde puedo. Como decís me parece mucho más claro que la intención es asignar una variable.

    Por ejemplo es muy común ver líneas de código:
    If Unidad1 = Unidad2
    &Factor = 1
    Else
    call(PConvertir, Unidad1, Unidad2, &Factor1)
    EndIf

    Acá claramente un udp quedaría mucho mejor.

    Además en las reglas de transacciones, Genexus arma mejor el árbol de llamadas al usar udp, porque usando call muchas veces no se da cuenta cual es el objetivo de la llamada (ya que tampoco aprovecha los in: y out:).

    ResponderEliminar
  9. De acuerdo con Enrique Almeida:

    &Result = pSuma(a,b)

    en Realidad las otras formas son bastante malas en cuanto a legibilidad. No entiendo si quiera por qué existen...

    ResponderEliminar
  10. Marcos:

    &Result=pSuma(&a,&b)

    funciona en GeneXus 9.0.

    Si fuera en GeneXus X, hubiera puesto

    &Result = Suma(&a, &b) :)

    ResponderEliminar
  11. Marcos... hay un deslizamiento de valor en la frase.

    El artículo de Coding Horror es muy bueno... y tu comentario me llevo a releerlo.

    Fijate en "Let us change our traditional attitude to the construction of programs: Instead of imagining that our main task is to instruct a computer what to do..."

    En particular me gustaría destacar "attitude" y "imaginig".

    La 'codificacion' es, en última instancia, una actividad en la que instruimos una computadora.

    Todos lo demás es accesorio... Lo cual no quiere decir que no sea importante. Es extremadamente importante.

    En particular podriamos subir un nivel y poner la programación en el contexto mas amplio del desarrollo de software... En lo personal me gusta mucho lo que escribe http://alistair.cockburn.us

    En uno de sus articulos dice

    “El desarrollo de software es un juego cooperativo, en el cual las personas emplean registros como apoyo para comunicar, rememorar e inspirar tanto a sí mismas como a otros participantes con el propósito de llevar a cabo el siguiente movimiento en el juego. El punto final del juego es un sistema de software en operación; el residuo del juego es un conjunto de registros que ayudarán a los jugadores a participar en el próximo juego. El juego siguiente es la modificación o el reemplazo del sistema, o quizá la creación de un sistema colindante.”

    Bonus track...
    http://alistair.cockburn.us/Software+development+as+community+poetry+writing

    saludos

    ResponderEliminar
  12. Enrique: Entonces se ve que es algo que agregaron post-U4, porque yo estoy con el U4 del Development Environment y el U4 del generador Java (Win), y no funciona...

    ResponderEliminar
  13. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  14. Esta te la acepto y estoy totalmente de acuerdo. Pero la de los 'where' indentados en los 'for each' no me la banco, jeje.

    Mi opción:

    for each
    where att1 = &a
    where att2 = &b
    _____&c = att3+att45
    _____&d = att4*att5
    endfor

    Tú opción:

    for each
    _____where att1 = &a
    _____where att2 = &b
    _____&c = att3+att45
    _____&d = att4*att5
    endfor

    Obviamente es muchisimo mas legible la mía no??

    ResponderEliminar
  15. Pah, Roberto, que tema ese. Se han peleado guerras por gente que no se pone de acuerdo como indentar el for each...

    Dos cosas:
    1) Mi opción no es la que decís... Mi opción es
    for each
    _____where att1 = &a
    _____where att2 = &b
    _____
    _____&c = att3+att45
    _____&d = att4*att5
    endfor

    Nótese la línea en blanco entre el último where y la primer línea de código. Parece poco, pero hace la diferencia, porque separa dos bloques que (creo que en esto estamos de acuerdo) deben quedar separados.

    2) El tema con tu opción, es que el for each no está aislado, está dentro de otro bloque de código.

    Si el for each queda separado de la línea anterior, entonces todo bien, no me molesta.

    Pero cuando está pegado a N líneas del bloque de código anterior, entonces a primera vista, parece que el bloque del for each empieza en la línea &c = att3+att45 en lugar de empezar en el for each como es realmente...

    Como conclusión, me parece más importante dejar una línea en blanco separando bloques (cuando vale la pena hacerlo), que preocuparse por como está indentando el for each.

    Ah, y solo para que quede registro, voy a seguir indentando los where un tab más que el for each :)

    ResponderEliminar
  16. Con mi equipo discutimos bastante... Al final pusimos como maxima prioridad la legibilidad (le gano a la postura de escribir poco ¿seria 'escribilidad'?).

    Como es FOR EACH es una operacion extremadamente costosa en terminos computacionales y compleja en funcionalidad decidimos hacer que sea evidente cada parte que lo compone y que era muy imporante seguir patrones visuales claros.
    Un ejemplo.

    Sub 'Algo Importante'
    __For each
    ____Order by
    ______atributo
    ____Where
    ______atributo1_____=_'xxx' and
    ______atributo2_____=_'yyy'
    ____Defined by
    ______atributo3
    __//Copiar datos
    ____&var_corta______=_atributo1
    ____&var_larga______=_atributo2

    ____&var_larguisima_=_atributo3
    ____&var_larga______=_atributo4
    __EndFor
    EndSub

    Sin duda mas incomodo para escribir, es menos compacto pero es mucho mas facil indentificar los elementos principales.

    En particular no usamos multiples WHERE, en su lugar usamos AND ya que es mas generico (sobre todo cuando tenemos OR y NOT) ademas las condiciones quedan expresadas igual que las "Conditions"

    Siempre tratamos de colocar los atributos en la parte izquierda de la comparacion y alineamos los signos igual. Esto es para que identifiquemos muy rapidamente los atributros involucrados.

    Incluso dejamos lineas en blanco para agrupar logicamente los atributos que estamos accediendo

    El cuerpo en si del FOR EACH lo prefijamos con un comentario descriptivo.

    Por ultimo tratamos de poner siempre los FOR EACH en SUB con un nombre descriptivo.

    En definitiva programar para facilitar la lectura nos lleva necesariamente a dificultar la escritura... Pero una vez que te acostubras a los patrones visuales mejora tremendamente la interpretacion de la intencion del programador y reduce los errores a un minimo.

    ResponderEliminar
  17. Eliminas los comentarios que no te gustan !!!
    Sos un trucho cripsino!!

    ResponderEliminar