Traducido por Roberto Alfredo Moré
Visual FoxPro tiene 32,767 áreas de trabajo disponibles... para cada sesión
de datos. Dado que dada formulario o conjunto de formularios, barra de herramientas
e informe tiene su propia sesión de datos, el número total de áreas de
trabajo disponibles es astronómico. Agreguemos a esto orientación a objetos
con clases e interfaces que responden a eventos donde el desarrollador tiene
muy poco control sobre qué código corre y cuándo, y usted tendrá la realización
de una pesadilla. ¿Cómo puede usted tener bajo control el área de trabajo en
este escenario?
Nunca dependa de cuál es el área de trabajo actual.
Ya sea si usted está diseñando una clase y está escribiendo código para uno de sus eventos o métodos, o si usted está escribiendo código en un evento o método de un objeto en un formulario, no dependa de que sea seleccionada ningún área de trabajo en particular. Siempre, explícitamente, selecciones el área de trabajo que su código necesita. Por ejemplo, si en el click de un botón de comando usted necesita mover el puntero de registros en la tabla Customer, usted debería escribir el código de esta manera,
SELECT Customer
IF NOT EOF()
SKIP
ELSE
GO BOTTOM
ENDIF
Este código no hace ninguna suposición sobre cuál es el área de trabajo actual. ¿Por qué es importante?. Aquí tenemos un escenario expandido. Usted diseñó este formulario para que sea el formulario de edición del cliente y los únicos controles que hay allí son algunos cuadros de texto para editar los campos de la tabla Customer. El código del click del botón no tiene un select explícito en él porque sólo hay una tabla en el entorno de datos del formulario.
No obstante, y los usuarios son magníficos creando "no obstantes", el usuario requiere más tarde que el mismo formulario sea usado para mostrar contactos de un cliente. Usted adiciona un grid para mostrar registros de la tabla de contactos. Entonces comienza el problema. Si el usuario hace click sobre el botón desde uno de los cuadros de texto todo anda bien, pero si hacen click sobre el botón cuando el foco estaba en el grid el puntero de registros se moverá en la tabla de contactos en lugar de la tabla de clientes.
Usando la técnica de la selección explícita mostrada más arriba, este problema jamás ocurre.
Siempre vuelva las cosas atrás cuando termine.
El ejemplo de código de antes es bueno para asegurarse que el botón trabaja en el área de trabajo correcta pero, ¿qué pasa con todos los otros eventos que pueden dispararse?. ¿No necesitan también ocurrir en un entorno conocido?. Sí, lo necesitan.
Hay una simple máxima que es extremadamente importante aquí, nunca suponga nada acerca del entorno en el cual su código se ejecutará y siempre ponga el entorno en el estado en el que lo encontró cuando comenzó su código. El código de arriba podría ser expandido como el ejemplo que sigue para lograr esto.
LOCAL lcAlias
lcAlias = ALIAS()
SELECT Customer
IF NOT EOF()
SKIP
ELSE
GO BOTTOM
ENDIF
IF EMPTY( lcAlias )
SELECT 0
ELSE
SELECT ( lcAlias )
ENDIF
¿Qué trabajo adicional está haciendo este código?. Primero declara una variable local, lcAlias. ¿Por qué local?- Porque existe siempre la posibilidad de que este código llame a otro código que también esté usando la misma variable y no queremos que esa variable sea modificada.
Luego, el código guarda el nombre del área de trabajo actualmente seleccionada en esta variable local. Al final, el código retorna el área de trabajo actual a lo que era cuando el código comenzó.
¿Qué hay acerca de otras cuestiones del entorno de datos?
Está bien, el código puede manjar el tema del área de trabajo pero, ¿qué pasa con otras cosas?. ¿Qué pasa si un código necesita asignar un índice específico o si requiere que el puntero de registros se mueva temporariamente?.
Estas cosas también pueden manejarse a través del mismo mecanismo. El código de más abajo muestra el proceso de guardar el puntero de registros, el índice, el área de trabajo y el retorno a los valores originales al final.
LOCAL lcAlias, lcOrder, lnRecno
* Guarda el área de trabajo
lcAlias = ALIAS()
SELECT Customer
* Guarda el número del índice actual
lcOrder = ORDER()
SET ORDER TO Name
* Guarda el número de registro actual
lnRecno = RECNO()
SEEK “Jones”
* Ejecuta algún proceso
* Ahora repone los valores iniciales
* Repone el número de índice para Customer
IF EMPTY( lcOrder )
SET ORDER TO 0
ELSE
SET ORDER TO &lcOrder
ENDIF
* Repone el puntero de registros
IF lnRecNo <> 0 AND NOT lnRecNo > RECCOUNT()
GOTO lnRecNo
ENDIF
* Repone el área de trabajo.
IF EMPTY( lcAlias )
SELECT 0
ELSE
SELECT ( lcAlias )
ENDIF
¿Cuál es el punto central de esto?
Codificando cada método, evento y procedimiento de esta manera usted nunca tendrá que preocuparse por cuál es el entorno de datos actual. Cualquier fragmento de código creará el entorno que él requiere y repondrá el entorno que encontró cuando empezó. Esta técnica hará su código mucho más fácil y no será afectado por la secuencia de eventos en tiempo de corrida. Las subrutinas no dependerán de qué rutina las llamó y éstas, a su vez, no serán afectadas por lo que hagan las subrutinas al entorno de datos.
Todo esto es muy bueno pero, ¿qué tiene que ver esto con la manera en que Visual FoxPro trabaja internamente?. Visual FoxPro es un sistema que reacciona a eventos. Las cosas ocurren y el código se ejecuta en base a aquellas cosas que ocurren. Los usuarios interactúan con los controles y formularios y su interacción dispara métodos de eventos. No hay realmente ningún buen método para que un desarrollador pueda saber exactamente que entorno de datos existirá cuando se ejecuta un fragmento particular de código. Diseñando los métodos de sus eventos, sus métodos y otro código de esta manera usted evitará que el entorno de datos sea una preocupación.
Estas técnicas pueden ser extendidas a otras cuestiones como los comandos SET. La función SET() le permite a usted obtener los valores actuales de estos comandos, los que pueden ser guardados y repuestos luego de su código haya cambiado las asignaciones y terminado su trabajo.
Jim Booth
Yo este tema lo he resuelto de una forma que hasta ahora me va realmente bien. Me he creado dentro de una clase form, (también es aplicable a los formset),, a partir de la cual creo todos los formularios de la aplicación, dentro de esta clase, me he creado una propiedad que es una matriz, llamada por ejemplo 'pila' y otra propiedad llamada 'pilaindex'. Luego me he creado dos métodos, llamados 'pushpila()' y 'poppila()'. cuando voy a trabajar con alguna tabla, ejecuto un pushpila(), el cual lo que hace es grabar en la matriz 'pila' el nombre del alias, actual así como el order y el puntero del registro en la matriz, y cuando termino de trabajar con la tabla, hago un 'poppila()' y éste hace lo contrario, extrae de la pila el alias, orden y puntero de la última tabla grabada, esto me permite despreocuparme sobre cuál fichero estaba antes seleccionado, pues cada pushpila(), graba el alias actual y lo puedo ejecutar tantas veces como quiera, eso sí, cada 'pushpila()' conlleva su correspondiente 'poppila()' para garantizar la coherencia del procedimiento
ResponderBorrar