martes, 6 de enero de 2009

Refactoring de código GeneXus

Hace tiempo que vengo pensando en el tema de refactoring en GeneXus. Hay varias cosas que me gustaría tener, que creo que simplificaría el trabajo en algunos casos.

Por "refactoring" me refiero a reorganizar el código sin cambiar la funcionalidad ni introducir errores, para que el código quede más legible, ordenado, conciso y reutilizable.

En la Wikipedia, se define code refactoring como:
Code refactoring is the process of changing a computer program's internal structure without modifying its external behavior or existing functionality. This is usually done to improve code readability, simplify code structure, change code to adhere to a given programming paradigm, improve maintainability, or improve extensibility.
GeneXus ya tiene alguna funcionalidad que podría entrar de esta categoría, como por ejemplo renombrar un objeto, o por qué no incluirlo, cambiar la estructura de una tabla, el tipo de datos de un campo o cualquier cosa que implique una reorganización en la base de datos.

Sin embargo hay cosas que le faltan y que estarían buenas. Paso a detallar.

Indentar el código
Creo que esto ya está bien resuelto en GeneXus X.... Para GeneXus 9, hay un programita que hizo Matías, que también resuelve el problema, aunque por fuera de GeneXus. Se puede bajar de GXOpen, aunque creo que todavía está en construcción.

Extraer una subrutina
Dado una porción de código seleccionado en el editor, poder pasar ese código a una subrutina (tiene que pedir el nombre), y dejar en el lugar del código la llamada a la misma.

Hay algunos temas a tener en cuenta que no son triviales. Los que se me ocurren ahora (seguramente hayan más) son:
  • Si el código que se extrae está dentro de un for each, puede ser que se usen atributos del for each exterior, que no serían válidos en el contexto de la subrutina. De alguna forma (¿viendo la especificación detallada?) se debería identificar estos atributos y cambiarlos por variables, que se inicializarían antes de invocar a la subrutina.
  • En principio el código seleccionado no tiene por que ser "consistente". Por ejemplo, podría seleccionar para extraer un bloque if pero sin seleccionar el endif, por lo que el código quedaría con errores. Otro caso es cuando elijo una sección de código que contenga la definición de un evento o de otra subrutina, o el comando Load si estoy en un web panel, etc.

Extraer un procedimiento
Es muy similar al caso anterior, pero con la dificultad adicional que hay que redefinir las variables en el procedimiento nuevo, e identificar cuales son los parámetros de entrada y de salida y cuales son las variables internas.

Reordenar eventos y subrutinas
Esto es más que nada un gusto personal. Si tengo un web panel que tiene eventos Start, Refresh, Load, Enter, y eventos de usuario, me gusta que queden en ese orden. Además me molesta que los eventos estén mezclados con las subrutinas. Se podría tener una opción que reordene los eventos, no parece demasiado difícil.

Ordenar las subrutinas puede ser un poco más complicado. Por ejemplo, el siguiente código:
do 'Inicializo'
do 'Proceso'
Sub 'Calculo'
Sub 'Proceso'
Sub 'Inicializo'  // llama a la subrutina 'Calculo'
¿Cómo debería ordenar las subrutinas? A mi me gusta: Inicializo, Proceso, Calculo. Creo que las dos primeras no hay mucha discusión, pero cuando empiezan a haber subrutinas que llaman a otras, puede complicarse un poco más. Por ejemplo, la subrutina Calculo que se llama solo desde Inicializo, la podría poner inmediatamente abajo de esta, para que quede lo más próximo posible a su uso.

De todas formas, no debería haber ciclos (sub A llama a sub B que llama a sub C que vuelve a llamara a sub A), por lo que se podría construir un grafo dirigido y ordenarlas según alguna recorrida del mismo.

Extraer data selector o data provider 
Debería ser igual que los casos de extraer subrutina o procedimiento, aunque personalmente me cuesta un poco más visualizarlo porque no he usado demasiado los data selectors ni los data providers. Es algo para pensar...


Reconocer patrones de código y extraer procedimiento/subrutina
Los casos anteriores para extraer subrutinas y procedimientos eran con intervención de un usuario. Este caso, mucho más complejo, sería de forma automática.

Hay varios niveles de dificultad:
  • Identificar código idéntico en la KB. Lo más difícil es saber que buscar, porque después compara para ver si es idéntico es trivial.
  • Me ha pasado encontrarme con bloques de código casi idénticos, pero que en algún lugar usan una variable distinta, o un literal distinto, pero el sentido del código es el mismo. En ese caso se podría extraer un procedimiento que reciba los parámetros que necesite y que quede con el código genérico.
  • La más difícil, es cuando se hacen cosas parecidas en dos lugares, pero en una de ellas se hace algo más, por ejemplo en una tengo un if sin else y en la otra tengo el else. Son firmes candidatos a unificar, pero parece complicado hacerlo de forma automática.
Conclusiones
Creo que hay muchas cosas para hacer en este sentido que nos facilitaría el trabajo.

La mayoría no son fáciles, pero tampoco parecen imposibles. Tal vez se preste para algún proyecto de grado de facultad, o para desarrollar una extensión con fines de lucro.

Si alguien tiene interés en hacer algo de esto, cuenta con todo mi apoyo.

4 comentarios:

  1. Mientras leía la primer parte de extraccion de una parte del codigo como subrutina, pensé en que se haga automático (que es lo que decis mas adelante), o talves que dada una seleccion de codigo (para pasar a sub) automaticamente se busque codigo similar para sustituir por el llamado a la subrutina.
    Esto me parece muy muy dificil.. Sabemos que nosotros introducimos errores al hacerlo.. no me imagino hacerlo automáticamente..

    ResponderEliminar
  2. Por eso está el viejo dicho: To err is human, but to really fuck up you need a computer! :)

    ResponderEliminar
  3. Buen post.
    Yo habia pensado 2 de tus opciones, la de busqueda de codigo "parecido" para unificarlo en uno solo, y esto incluye objetos que tengan parametros "parecidos" y una parte de la navegacion similar, para hacer una primer seleccion. Luego hay que hacer una comparacion mucho mas inteligente para ver si el codigo es unificable.

    Tambien me interesaba el problema de sustituir parte de las navegaciones por Data Selectors. Es un problema interesante para resolver y creo que nos puede servir muchisimo.

    ResponderEliminar
  4. no hay gente que se interese en esto?, podríamos formar un grupo de trabajo y tratar de hacer algo.. no?

    ramunozp@gmail.com

    saludos.

    ResponderEliminar