|
|
|||||||||
Sí puedes leer esta frase, la página ha sido actualizada. |
| El Amiga Me Encanta ha conseguido el permiso por escrito de IDG Comunications España para ofrecer los artículos de la revista Amiga World España. |
|
Por Robert J. Mical Escribir un programa que actúe bien en un entorno libre de errores es relativamente fácil, si usted puede encontrar parecida situación. Los errores afloran y el programa crece en complejidad. Y cuando surge un error o una condición que lo aborta se hace cada vez más difícil salir graciosamente. Pasadas las frustraciones que me han ayudado a desarrollar un estilo de programación en el cual yo construyo programas que salen limpiamente de vuelta al sistema, sin importarle lo que causó la salida o si el programa que corría estaba total o parcialmente completo. Tanto la disciplina como el estilo tienen éstas dos reglas cardinales:
La parte más difícil de seguir la disciplina siendo consecuente es, asegurarse de que siempre se escribe el código que se está suponiendo, sin embargo, una vez que ya se ha cogido el ritmo de los pasos requeridos, esto se hace natural y usted automáticamente creará códigos bastante potentes y libres de roturas. (Mientras que los ejemplos que hay aquí están escritos en C, los conceptos y el estilo se pueden aplicar a todos los lenguajes). Como usted verá, yo estoy proponiendo mantener simples todas las cosas, incluso cuando esto signifique hacer cosas bastante amplias, asegurando el camino mejor que tomando atajos. El camino fácil de entender muchas veces se transforma en el camino más grande, pero por conveniencia, legibilidad y la estética completa de programar "a grandes trazos" está, en mi mente, bien merecido el pequeño incremento en la longitud del código y la disminución en el acabado. Usted puede siempre reducir el código durante las pruebas. Bajo esto tan simple mis programas principales tendrán este aspecto: VOID principal ()
<
if (init()) DoMainTask();
CierraTodo();
>
Incluso mi madre podría entender esto: primero Init() ejecuta las inicializaciones del programa, incluyendo el intentar repartir los recursos del sistema. Si alguna de las inicializaciones falla, entonces Init() devuelve el valor BOOL con FALSE (falso). Si todas las inicializaciones se hubieran realizado satisfactoriamente, entonces Init() devolvería el valor TRUE (correcto). SI Init() es correcto, entonces se realizaría la tarea principal DoMainTask(). Finalmente, en todos los casos se va a CierraTodo() para cerrar y liberar todas las cosas, devolviendo los recursos repartidos al sistema. Principal() es sólo un micro-ejemplo de la disciplina de las salidas graciosas. En Init() y DoMainTask() se toman los recursos, y en CierraTodo() estos se devuelven. En suma, el programa abandona la ejecución tan pronto como es detectado un error [ no va a DoMainTask() si Init() falla]. Permítame mostrarle algunos ejemplos de las rutinas Init() y CierreTodo(). /* Declara las variables globales de los recursos
* que tiene valores NULL */
struct IntuitionBase *IntuitionBase = NULL;
struct Window *WorldWindow = NULL;
struct Remember *GlobalKey = NULL;
BOOL init()
/* Devuelve TRUE si todos los inicios se
* cumplen, sino devuelve FALSE */
<
BOOL Devvalor;
/* Coloca el valor FALSE hasta que no se este
* seguro de que todo está bien */
Devvalor = FALSE
/* Hace las inicializaciones. SI falla una va a
* DONE */
if ((IntuitionBase = (struct IntuitionBase *)
OpenLibrary("intuition.library",0))== NULL)
<
printf("error: no puedo abrir la libreria intuition"):goto DONE;
>
/* otro ejemplo de init (rutina definida abajo)*/
if (NOT AllocBuffers()) goto DONE;
/* todos los inicios se han desarrollado
* satisfactoriamente, así que devuelve TRUE */
Devvalor = TRUE;
DONE:
return(Devvalor);
>
CierraTodo()
/* Examina que recursos del sistema fueron
* repartidos y los librera */
<
if (IntuitionBase)
<
/* Nosotros abrimos la libreria. Ordenamos
* repartir * cualquier otro recurso específico
* de intuition ? */
if (WorldWindow)
<
CloseWIndow(WOrldWIndow);
WorldWindow = NULL;
>
FreeRemember(&GlobalKey, TRUE);
CloseLibrary(IntuitionBase);
IntuitionBase = NULL;
>
>
Como muestra este ejemplo, usted puede usar variables globales como indicadores para seguir la pista de los recursos que han sido repartidos. Usted comience inicializando las variables globales de los recursos de sus sistema mediante valores NULL. Las variables declaradas de esta manera sólo tendrán valores no-NULL si los recursos del sistema han sido repartidos. Detectando si el valor es no-NULL usted puede determinar que recursos necesita ser liberado. Considere la variable IntuitionBase en el ejemplo de arriba. Si IntuitionBase es NULL, entonces la librería nunca ha sido abierta, y los recursos específicos de intuition pueden ser ignorados. Si IntuitionBase es no-NULL, entonces la librería ha asido abierta satisfactoriamente, así usted debe cerrar ésta. En suma, usted deberá chequear las variables específicas de intuition para los valores no NULL, y si encuentra alguno, deberá también liberar sus recursos. Tenga en cuenta que yo puse de nuevo las variables a NULL después, y liberé los recursos asociados con ellas. Si usted está seguro de que su programa nunca hará alguna cosa después de que llame a CierreTodo(), entonces esto será innecesario. Yo nunca estoy seguro, sin embargo, esto me recordará mañana las cosas que me prometo a mi mismo hoy. Mañana puedo llamar accidentalmente más de una vez a CierraTodo() para efectuar un reinicio frío del programa. De ésta manera eligiendo hacer las cosas del camino seguro, yo borro todas las variables a NULL. Este método de seguir la pista a los recursos es parte del ritmo de este estilo de las "salidas graciosas". Yo sigo siempre este ritmo, siguiendo siempre las reglas de la misma manera, y esto siempre funciona. Hay rutinas de bajo nivel que chequean los errores del sistema (que no pueden abrir un fichero dado, que no pueden reorganizar la memoria) y rutinas de alto nivel que chequean si las rutinas de bajo nivel fallan. Las rutinas de bajo nivel siempre devuelven un valor que indica el éxito o el fallo (las rutinas BOOL devolverán TRUE o FALSE, mientras que una rutina para la repartición de la memoria deberá devolver un puntero de memoria o NULL). Cuando una rutina de bajo nivel pueden ellas mismas devolver un valor que indique éxito o fallo, o pueden simplemente cesar la ejecución y retornar tan pronto como un error ocurra. Las rutinas de alto nivel normalmente no imprimen un mensaje. Por ejemplo una rutina que su propósito sea el de repartir la memoria deberá ser escrita para imprimir un mensaje de error en caso de que falle. Como la siguiente rutina: /* Las variables de los recursos son NULL a
* no ser que sus * buffers asociados hayan sido
* repartidos */
UBYTE *buf1ptr = NULL;
UBYTE *buf2ptr = NULL;
UBYTE *XAllocMem(size, types)
LONG size, types;
/* intenta distribuir la memoria especificada,
* imprime un mensaje de error si falla, devuelve
* el puntero de memoria o NULL */
<
UBYTE *ptr;
if ((ptr = AllocMem(size, types)) == NULL_
printf("error:no hay memoria");
return(ptr);
>
BOOL AllocBuffers()
/* si todos los buffers están distribuidos devuelve
* TRUE y sino FALSE */
<
BOOL Devvalor;
devvalor = FALSE;
if ((buf1ptr = XAllocMem(BUF1_SIZE, BUF1_TYPES))
== NULL) goto DONE;
if ((buf2ptr = XAllocMem(BUF2_SIZE, BUF2_TYPES))
== NULL) goto DONE;
devvalor =TRUE;
DONE:
return(devvalor);
>
Y añade éstas sentencias a la rutina CierraTodo(): if (buf1ptr) < FreeMem(Buf1ptr, BUF1_SIZE); Buff1ptr= NULL; > if (buf2ptr) < FreeMem(BUf2ptr, BUF2_SIZE); Buf2ptr = NULL; > Usted se habrá dado cuenta que en las rutinas AllocBuffers() y Init() que yo he usado los temerosos comandos Goto, los cuales muchas veces causan la controversia entre los programadores porque estos están en contra del estilo estructurado de programación. Actualmente Goto es el único aspecto no estructurado de este estilo, y yo lo uso de una manera estructurada. En ambos estilos, la lógica del programa siempre circula a través de las rutinas desde la parte superior hasta la inferior, excepto por los ciclos for() y while(). En este estilo, el Goto está permitido tan lejos como la destinación este debajo del comando, manteniendo por eso el fluir descendente del control a través de la rutina. Ambos estilos tratan de evitar la pesadilla del código "spaghetti". El uso consistente de Goto hace el código más fácil de escribir y de entender. Permítame examinar las rutinas de retorno en caso de error más detenidamente. Cuando una rutina va a devolver el resultado que podría indicar un error, así como en AllocBuffer() y Init(), el resultado que devuelve la variable inicializada es cambiado a un resultado de fallo al principio del código. Cualquier error desde dentro del cuerpo de la rutina causa al control ir al final de esa rutina, donde el resultado del error es devuelto. La rutina de retorno en caso de error es uno de los bloques básicos del estilo de éstas "salidas graciosas". Eliminar las funciones BOOL es de hecho parte de el ritmo. Yo cree muchas veces éstas rutinas de detección de errores hasta que tuve definido unos macros en mi editor de texto favorito (Cygnus-Ed) en orden para añadir la función BOOL a mi código fuente. Yo tecleo lo siguiente: BOOL Mumble() < > y entonces pulso la tecla de función que añade un bloque de texto para crear esto: BOOL Mumble() < BOOL devvalor; devvalor = FALSE; devvalor = TRUE; DONE: return(devvalor); > Y voilá. Entonces yo relleno el cuerpo de la rutina usando "goto DONE" donde ha ocurrido un error. Debido a que yo hago esto con todas las rutinas que se pueden interrumpir o fallar, un resultado de error puede ser fácilmente propagado de vuelta al principio -sin tener que estar ejecutando más programas lógicos de esta manera- no ocurre como con rutinas de muchos niveles de profundidad las cuales se consiguen llamando a otras rutinas. Usted puede pensar que no volverá a mirar un trozo de código, entonces, usted estará atormentado durante años, así pues autodisciplínese para hacer de las técnicas seguras una parte de su estilo de programación. Usted será un mejor y feliz programador si usted hace esto. |
| Envía esta página web a un amigo: Esta opción está desactivada temporalmente, rogamos disculpen las molestias |
|