código escrito · artículos digitales de informática
 
De lo general a lo particular
04.07.2003 :: Jaime Irurzun

El pasado día 15 de Junio, mientras volvía en coche con Bingen Ugaldebere y Juan José García del V Congreso del Grupo Olivares 2000, Bingen me estuvo contando una técnica que utiliza al programar. Sin planteármela muy en serio, sí que poco a poco me había ido dando cuenta de que era algo que había que aplicar, dada su gran utilidad, pero hasta ese viaje no me había dado cuenta realmente de lo útil que puede llegar a ser. Supongo que esto es algo básico para todo programador, y que muchos – si no todos – lo conocéis y lo ponéis en práctica a diario.

La técnica consiste en programar la mayor parte del código fuente de los programas de forma general. Es decir, que el mayor número de funciones que creemos, tienen que servir para cualquier caso, para cualquier programa. Los parámetros que la función recibe son los que la “amoldan” al caso concreto. De esta forma, la parte de código “particular” de cada programa se reduce increíblemente, y por lo tanto el tiempo de desarrollo de éste.

Lo básico es que hay que tener claro que aunque al escribir una función, ésta nos vaya a ocupar el doble o triple de número de líneas, si con ello conseguimos que sirva para cualquier programa, merece la pena. También probablemente aumentará mucho el número de parámetros que recibirá la función, pero no importa, sigue mereciendo la pena.

Yo por ejemplo he pensado que un proceso que se utiliza cantidad de veces y que merece la pena “modularizar” (no sé si es correcto el término), es el de reindexación. Por eso me he creado una función a la que le envío dos parámetros: el alias de la tabla que quiero reindexar (cAlias), y una variable lógica que indica si quiero que al final del procedimiento la tabla quede abierta (true), o si prefiero que quede cerrada (false). Ella sola se ocupa de crear un cuadro de diálogo con un meter, de cerrar la tabla y borrar el archivo índice si hace falta, de consultar en una dbf que tengo los TAGs que tiene que crear, y de cerrar el diálogo con el meter al final.

Otra de las ventajas de este método es que utilizando la técnica de “refactorización” que aconseja la Programación Extrema (XP), (revisar, depurar y mejorar el código fuente cada cierto tiempo para mejorar su legibilidad, limpieza y calidad), si almacenas en una carpeta común los archivos fuente de estas funciones, bastará con llamarlos desde la compilación de cualquiera de tus programas y automáticamente todos los cambios se implementarán.

Este ejemplo ha sido bastante sencillo. Supongo que todos tendréis vuestra función para reindexar, pero por si acaso me gustaría haberla podido publicar. Sin embargo, desgraciadamente el otro día me puse a rehacerla y la he fastidiado, de modo que prefiero no publicarla. Queda como tarea pendiente, si en futuros posts no aparece, recordármelo.

Gracias Bingen.

comentarios (11) |


Comentarios del artículo
1 · TuXsY · 04.07.2003

Mira, debes procurar que tus programas dejen como "resíduo" una buena biblioteca de funciones. Cuando al finalizar un programa tengas la sensación: "eso ya no lo voy a tener que volver a hacer"; entonces és que has programado bien ;-).

Bueno, éstas no son las consideraciones más importantes para la buena programación pero sí para una cosa indispensable para alguien que quiera programar en serio/serie, la reutilización de código.

2 · Manuel Calero · 04.07.2003

Jaime según la programación extrema, no hay q tender a la generalización puede ser un error, me explico, tratas de hacer fnciones para q realizen muchas tareas, pero seguro q mañana necesitas una tarea q no esta, q haces ? amplias la función o escribes una nueva funcion q te resuelva ese tema puntual.

Ummmmm, no se le respuesta correcta pero si aplicamos un principio de XP la simplicidad yo optaria por la segunda.

Pos supuesto si conseguimos los dos casos, simplicidad y generalidad mejor q mejor, pero no hay q hacer funciones con 20 parametros q hagan un poco de todo, mejor 20 funciones con 1 parametro. Es mi opinión.

Saludos y enhorabuena con tu nuevo Blog.

3 · JAcinto · 04.07.2003

No estoy muy de de acuerdo con tu planteamiento.

Mas que ampliar el numero de lineas de la funcion, prefiora utilizar directivas tipo OVERLOAD, y si no es posible, al menos de una funcion que haga de router hacia las funciones concretas.

Tampoco me gusta el planteamiento de "Refactorizar", creo mas adecuado que la documentacion acompañe al codigo durante todo el porceso de creacion, las modificaciones posteriores ocasionan los problemas que te han pasado a ti mismo, que casca y no sabes porque, y aun gracias si detectas el fallo de inmediato.

En cuanto a las funciones de reindexacion, si son llamadas por el usuario lo encuentro superfluo e ineficaz, es el propio sistema el que debe de detectar el problema y actuar adecuadamente para recuperar la situacion para casos de problemas de indices, y estoy incluyendo en ello a CLIPPER

4 · Jaime Irurzun · 04.07.2003

Manuel,
No sé si me he explicado del todo bien...
En el artículo, al decir que no importa que la función tenga el doble o triple de número de líneas, si con esto se consigue que sirva para cualquier caso, no me refiero a que haya que escribir funciones que ejecuten MUCHOS procesos. Al contrario, la función creo que es mejor que realice UNA TAREA concreta, pero que lo que no tiene que importar es que se alargue, si con esto se consigue que sirva para cualquier caso. Evidentemente cuanto más corta mejor, y más clara. A eso me refería con la refactorización del XP. De todas formas, aún no tengo mucha idea del XP, ya que tengo el libro pero está en la lista de "libros pendientes de leer" ;-)
Entonces, el problema que dices de necesitar una tarea que no está en la función desaparece, habría que crear una nueva función para ese caso puntual, intentando que pueda servir para otros programas que requieran esa tarea, y si es demasiado concreta, pues se escribe para ESE programa solamente.

Jacinto,
No tengo mucha idea de lo que dices porque no conozco las directivas tipo OVERLOAD. Y en cuanto a lo de reindexar... pues escribir una función que te sirva para reindexar una tabla no implica que tenga que ser llamada por el usuario, aunque creo que está bien la opción en el menú por si acaso. Esa función es útil sobre todo para llamarla tú desde cualquier parte del programa. Claro que entonces el meter igual es un poco absurdo... igual estaría bien añadir un parámetro que indique si quieres que se reindexe con meter o no.

Esa es mi opinión, si me la haceis cambiar a mejor, encantado :-)

5 · Armando Estrada Bucio · 04.07.2003

Jaime:

Me parece muy interesante tu artículo y coincido en la mayor parte pues ahora que lo leo me doy cuenta que de manera inconciente así he tratado de realizar mis aplicaciones. Solo quiero hacer una sugerencia, cuando dices:

"si almacenas en una carpeta común los archivos fuente de estas funciones, bastará con llamarlos desde la compilación de cualquiera de tus programas y automáticamente todos los cambios se implementarán"

Lo que yo hago es crearme mi propia .LIB que enlazo con las demas libs a mi aplicación y todo funciona igual. Recuerda que el .EXE solo se incrementará en cuanto al número de funciones que utilice y no por el total de bytes que pese la LIB.

6 · Jaime Irurzun · 04.07.2003

Armando,
En realidad la idea es la misma. Eso lo he pensado más de una vez en hacer, sin embargo la ventaja que tiene no crear la LIB es que en cualquier momento puedes modificar algo de esos archivos fuente comunes para todos los programas. De esta forma te evitas tener que crear la LIB cada vez que cambies algo, aunque posiblemente tus funciones comunes lleven años de depuración, y por eso no las modificas nunca. Las mías están recien comenzadas, y por eso aun las modifico casi a diario :-)

7 · Jacinto · 04.07.2003

El concepto de Refactorizacion lo veo util para programacion independioentemente de cual sea esta, a fin de cuentas, la programacion es la cuantificacion y definicion del flujo de datos de un proceso.

La directiva OVERLOAD esta en C y en Delphi, y me permite funciones como estas:
DECLARACION:
procedure GetTickLib(cTick: string; nTipPer, nFromPeriodo, nPeriod: integer;
var oSerieValor: TLineaGraf); overload;
procedure GetTickLib(cTick: string; nTipPer, nPeriod: integer;
var oSerieValor: TLineaGraf); overload;
-----------------------------------------
En tiempo de ejecucion, esta se bifurca a la funcion que coincide en numero de parametros y tipo con la llamada.

Es extremadamente util para evolucionar codigo a nuevas prestaciones pero mantener la compatibilidad con el codigo antiguo sin necesidad de modificar las llamadas ni complicar el codigo de la funcion final, en cierta manera es una "factorizacion" del crecimiento.

8 · José Luis Sánchez · 05.07.2003

Pues yo estoy en la linea de Manuel Calero. No soy partidario de funciones generalistas, que son tan grandes que luego ni te acuerdas de que hacen y, lo que es peor, la tocas para un programa y las estropeas para las demás.
Saludos,

9 · Manuel Calero · 06.07.2003

El concpto de overload en clipper no existe, entre otras cosas xq no hay strong-typing y el compilador no puede saber de q tipo es una variabel en tiempo de compilación. Sin embargo yo si uso tecnicas de Overload en mis funciones, os explicare como, creo q con un ejemplo bastara.

Function cNomCli( cCodCli, uDbfCli )

if valtype( uDbfCli ) == "O"
uDbfCli:Seek( cCodCli )
else
( uDbfCli )->( dbSeek( cCodCli ) )
end if

return ( ... )

en fin esta es mi idea.

Saludos.

10 · Fernando · 13.07.2003

Bueno, yo de Clipper ni idea, pero si algo de algun que otro lenguaje...
Un ejemplo que creo ilustra bien lo que el amigo dice es la función de python os.path.join... esta funcion soporta dos argumentos, dos cadenas de texto, que unirá formando una ruta válida según el sistema operativo del entorno (Python es multiplataforma...).
Me encontré traduciendo un programa de Python a PHP, y dicha función no existe en esté último... asi que me veia forzado a concatenar cadenas cada vez que encontrase el os.path.join o me creaba mi propio pathjoin:

  function path_join() {
    $path = '';
    foreach (func_get_args() as $arg) {
      $path .= '/' . $arg;
    }
    return substr($path, 1);
  }

PHP permite lista de argumentos variables, y maneja arrays de una manera espectacular, asi que con cuatro líneas implementé la función (válida sólo para plataformas windows o *nix) mejorandola...
Si quisiera mejorarla añadiria soporte para detección de OS y crear las rutas según el caso... pero PHP corre fundamentalmente bajo plataformas Win o *nix (en MAC bajo Max OS X... que está basado en unix...) asi que la función ha de ser perfectamente válida para el 99% de los casos y me costó 5 minutos crearla ;-)

Saludos

11 · Al González · 28.11.2003

Buscando en www.yahoo.com "Biblioteca de funciones"+Delphi, me encontré con este interesante artículo de Jaime Irurzun, y de pronto la globalización me sorprendió. Por un momento me sentí conectado con Bingen Ugaldebere.

Coincido con los que están a favor de la generalización del código, que plantea Jaime.

Y con respecto a lo que comentan Manuel Calero
José Luis Sánchez y Jacinto. Debo decir que si existe una alternativa viable (porque la vengo implementando desde hace años), para los dilemas señalados:

La Programación Orientada a Cero (POC).

Es una técnica de programación que empleo desde hace más de 10 años, pero que apenas hace tres o cuatro bauticé con ese nombre. Es un superconjunto de la técnica de Generalización aquí planteada. El ingrediente extra es: la atomización de código (o programación atómica).

La atomización consiste en dividir en el mayor número de funciones posibles el código de la biblioteca. Es así como se puede conseguir un producto a la medida particular del cliente, hecho con una biblioteca de propósito general. La arena ocupa mejor el espacio de un cubo, que las rocas de mayor tamaño.

Uno de los principios de la POC, es precisamente lo que comenta Jaime al principio: la mayor parte del código desarrollado para una aplicación, debe quedar en elementos reutilizables dentro de una biblioteca de propósito general.

Los tres principios fundamentales de la POC son:
Centralización
Atomización
Gestión de bibliotecas

Bajo esta técnica desarrollé la biblioteca Interfaz GH para Delphi. Contiene más de mil funciones de propósito general, que hoy en día me ahorran cientos de horas de programación en cada proyecto de software.

Interfaz GH va creciendo con cada aplicación que desarrollo y la comparto con la comunidad global de desarrolladores en
el foro Programadores Delphi de México (http://groups.msn.com/ce77cj5fut58ai6vahamsb3nb1)

Ahí mismo se encuentra un artículo más amplio sobre lo que es la Programación Orientada a Cero:
http://groups.msn.com/ce77cj5fut58ai6vahamsb3nb1/poc.msnw

Por último, debo agregar que no sólo debe aplicarse la POC siempre que sea posible (algunos compiladores no soportan bien la atomización), sino que también debe emplearse una Gramática de Desarrollo de Software, es decir, una serie de normas que nos dicten cómo debemos nombrar a una función, tipo de dato, variable, etc., o si debemos poner espacio o no después de coma, etc., ya que estos pequeños pero importantes detalles nos ayudan a hacer más legible nuestro código fuente, y por ende, su depuración es más efectiva.

Espero seguir en contacto con ustedes.

Atentamente,

Al González.
Programadores Delphi de México (http://groups.msn.com/ce77cj5fut58ai6vahamsb3nb1)














































Creative Commons - Jaime Irurzun y Aitor Martin