30 de agosto de 2016

Herencia y Polimorfismo con Visual FoxPro

Una de las ventajas que nos dá el uso de clases es el ahorro de programación y el mantenimiento centralizado de códigos que antes podían estár regados (y repetidos) en tu aplicación. Es sencillo, tener tu código lo más general posible, ésto se hace con el uso de propiedades y métodos personalizados por medio de parámetros. Es decir, puede que en cierto lugar establezcas por ejemplo, la tabla base en la que trabajarás, y después, aplicarle métodos con parámetros. Esto es con respecto a herencia, para tratar de que lo que ya escribiste, te sirva en más ocasiones.

Otra función de las clases, es dejar que éstas hagan el código genérico, y después de ello, aplicarle el código específico, esto se hace con la función DoDefault() y el comando NODEFAULT... A continuación un pequeño ejemplo:
oForm = CREATEOBJECT("MyForm")
oForm.SHow(1)
DEFINE CLASS MyForm AS cAbstractForm
   cFormName = "Client Form"
   PROCEDURE cmdAceptar.Click
      DODEFAULT()
      ***** Procedimiento despues de tu codigo base
      MESSAGEBOX("MyForm.Class Message")
   ENDPROC
   PROCEDURE DoAction
     LPARAMETERS tcAction
        MESSAGEBOX("Codigo antes de...")
        DODEFAULT(tcAction)
   ENDPROC     
   PROCEDURE DoExitValidation
      IF DODEFAULT()
         lcMessage = "Reconfirma que quiere salir"
         RETURN (MESSAGEBOX(lcMessage,4+32,this.cFormName)==6)
      ELSE
         RETURN .F.
      ENDIF
   ENDPROC   
ENDDEFINE

DEFINE CLASS cAbstractForm AS FORM
  cExitMessage = "Salir del Formulario"
  ADD OBJECT cmdAceptar AS CommandButton ;
     WITH Top = 200, Left = 150, Caption = "Aceptar",;
          Height = 35
  ADD OBJECT cmdSalir AS CommandButton ;
     WITH Top = 200, Left = 250, Caption = "Salir",;
          Height = 35    
  PROCEDURE cmdAceptar.Click
      Thisform.doAction("Aceptar")
  ENDPROC            
  PROCEDURE cmdSalir.Click
      Thisform.DoAction("Salir")
  ENDPROC
  PROCEDURE DoAction
    LPARAMETERS tcAction
       DO CASE 
          CASE tcAction = "Aceptar"
              *** Simular acción de aceptar
             MESSAGEBOX(tcAction)
          CASE tcAction = "Salir"
             IF  This.DoExitValidation()
                 *** Si ejecutó correctamente su validación, salir del formulario
               This.Release()
             ENDIF
          OTHERWISE 
             MESSAGEBOX("UnHandled Message")   
       ENDCASE        
  ENDPROC
  PROCEDURE DoExitValidation
     RETURN (MESSAGEBOX(this.cExitMessage,4+32,"eSoft Msg") = 6)
  ENDPRO
  PROCEDURE QueryUnload
     IF This.DoExitValidation()
       *** Dejar que haga lo suyo por defecto, salir del form
        DODEFAULT()
     ELSE
        *** Inhibir el comportamiento normal, evita que se cierre el Formulario.
        NODEFAULT
     ENDIF
  ENDPROC
ENDDEFINE
Copia el código anterior, pégalo en tu Command Window, seleccionalo en tu ventana de comandos y presiona ENTER, verás en ejecución un Formulario sencillo, que no tiene más que dos botones uno de aceptar y salir, veamos que es lo que hemos hecho ( y mira que no es poco y quizás te enrede mas...)

He creado una clase abstracta basada en Formulario, en ella se tiene un método general, llamado DoAction, el cual es llamado por los eventos Click de los botones de comando, pasando el parámetro de que acción realizará. También he tenido un método llamado DoExitValidation, éste método se supone hará alguna validación antes de querer salir del formulario, ya sea presionando el botón Salir o haciendo click en el botón de cerrar (esquina superior derecha de todos los formularios), o presionando CTRL+F4, se te das cuenta, el método DoAction al darle como parámetro "Salir" mandará a llamar el método doExitValidation... También tengo un código genérico para el momento de salir, implementado en el evento QueryUnload, mismo que dependerá de DoExitValidation...

Con respecto a nuestra clase heredada de cAbstractForm, le llamamos MyForm, en ella se hace la implementación de todo lo que hemos hecho en la clase base, he puesto varios MessageBox para que se vea el trayecto de cómo se ejecutan las cosas, observemos qué he hecho en nuestras herencias...

MyForm::cmdAceptar.Click() -> Hago que se ejecute cualquiera que sea el código en la clase base, después, ejecuto lo que deseo.

MyForm::doAction() -> Tambien ejecuto código antes de, después hago que se ejecute lo necesario, pasando a la clase base, el parámetro necesario, nótese que ésto afecta el qué se ejecuta en los eventos Click de cmdAceptar y cmdSalir.

MyForm::DoExitValidation() -> Esto es lo más interesante, hago que se ejecute el método base, de ahí, tomo el valor retornado por AbstracForm::DoExitValidation y ejecuto código extra, el cual cambiará el comportamiento de el formulario MyForm, pero no tendrá consecuencias con respecto a lo que necesita el método cAbstractForm::QueryUnload y cAbstractForm::doAction.Salir ya que retorno el mismo tipo de datos...

Así pues, ahi tienes un pequeño ejemplo minimalista de cómo darle herencia y polimorfismo. Espero te sirva.

Espartaco Palma Martínez

25 de agosto de 2016

Usando WSH para enviar secuencias de tecla en VFP

Muchas veces encontramos resuelta cierta funcionalidad que necesitamos para nuestra aplicación en algún software y nos preguntamos ¿cómo podría integrarlo a mi sistema?

En muchos casos la respuesta la encontramos en las API que dicho software pone a nuestra disposición o a veces un control ActiveX que podemos instanciar en nuestros formularios. Pero existen otros casos en los cuales no tenemos una respuesta positiva y tenemos dos alternativas: desarrollamos la funcionalidad requerida o intentamos automatizar el uso de dicho soft.

Veamos entonces como podemos utilizar la potencia del Windows Scripting Host para enviar la secuencia de teclas necesarias a otra aplicación para resolver cierta funcionalidad.

A modo de ejemplo automatizaremos una operación matemática en la aplicación Calculator de Windows. Para ello escriba el siguiente código:

MODIFY COMMAND SendKeys
Copie y pegue el código que se muestra a continuación:
* Listado Completo
Local lnHWND, loWSH

* Declaramos funciones de las API de Windows
DECLARE LONG FindWindow IN WIN32API AS FindWindow STRING @a, STRING @b
DECLARE LONG SetForegroundWindow IN WIN32API LONG

* Buscamos una instancia de la aplicación para obtener su Handler
lnHWND = FindWindow(0, "Calculator")
If lnHWND = 0
    * Si no se está ejecutando, la ejecutamos
    Run /N Calc.EXE
    * Y obtenemos su Handler
    lnHWND = FindWindow(0, "Calculator")
Endif

* Instanciamos el Windows Scripting Host
loWSH = CreateObject("WScript.Shell")

* Enviamos la aplicación a primer plano
SetForegroundWindow(lnHWND)

* Por último enviamos la secuencia de teclas
loWSH.SendKeys("140{+}200")
loWSH.SendKeys("{enter}") 
Secuencias de Escape

En la siguiente línea loWSH.SendKeys("140{+}200") vemos que el signo de suma está encerrado entre llaves. Esto se debe a que está definiendo una secuencia de escape, indicándole al WSH que debe enviar el caracter "+", y debe hacerse de esta forma ya que un signo "+" sin llaves estaría indicando que se debe enviar la pulsación de la tecla SHIFT (por ejemplo: "+casa", enviaría "Casa"). Vea Material Adicional para obtener la lista completa de secuencias de escape.

Conclusión

Si bien el ejemplo es muy sencillo y probablemente nunca se nos va a ocurrir automatizar la calculadora para hacer una suma, vale la pena analizarlo ya que muestra claramente el uso del método SendKeys del WSH.

Material Adicional

Refiérase a http://msdn.microsoft.com/library/default.asp?url=/library/en-us/script56/html/wsmthsendkeys.asp para consultar las secuencias de escape definidas para el método SendKeys del WSH.

Acerca del autor

Esteban Bruno nació el 25 de marzo de 1973 en Buenos Aires, Argentina. En el año 1992 se recibió de Analista Programado en la Comisión Argentina de Informática, y en 1998 egresó de la Universidad CAECE con el título de Licenciado en Sistemas. Desde el año 1990 ha trabajado en el área de desarrollo en diferentes empresas y utilizando una amplia gama de lenguanjes (Cobol, C, Clipper, FoxBase, FoxPro desde 2.5 para DOS hasta Visual FoxPro 9, Visual Basic, ASP, Java, etc.) y tecnologías. Es socio del MUG Argentina (Microsoft User Group) y actualmente se desempeña como Analista Funcional en IMR S.A. y dirige el Dpto. de Sistemas de TASSO S.R.L. Contacto: bruno@tasso.com.ar

19 de agosto de 2016

Conversion de Rutas (Path) de largas a cortas y viceversa

Este ejemplo muestra como convertir una ruta larga (formato Windows) a una ruta corta (formato 8.3 MS-DOS) y como revertirlo, llevarla de formato de ruta corta a ruta larga.

*######################################################
*# Llevado de Visual Basic a Visual Foxpro por Int21
*######################################################

*#########################################################
*# Declaracion de las API'S
*#########################################################
DECLARE INTEGER GetShortPathName IN kernel32 ;
STRING @lpszLongPath , STRING @lpszShortPath, INTEGER @cchBuffer
DECLARE INTEGER GetLongPathName IN kernel32 ;
STRING @lpszShortPath, STRING @lpszLongPath, INTEGER @cchBuffer

*#########################################################
*# Convertir una Ruta Larga dentro del equivalente 8.3 de ruta corta
*#########################################################
LOCAL lLen, GetShortPath, sLongPath, La_Ruta
lLen = 0
GetShortPath = ""
sLongPath = "C:\Documents and Settings\Morfeus\Escritorio\Long__-__S1227648262002"
*#### Establecer el buffer para la llamada  a la API
GetShortPath = SPACE(1024)
*#### Llamada a la API, Retirar los Caracteres no deseados y devolver la Ruta
lLen = GetShortPathName(@sLongPath, @GetShortPath, LEN(GetShortPath))
La_Ruta = LEFT(GetShortPath, lLen)
WAIT WINDOWS La_Ruta

*#########################################################
*# Convertir una Ruta Corta en su equivalente de Ruta Larga
*#########################################################
LOCAL lLen, sShortPath, LaRuta
lLen = 0
sShortPath = "C:\Docume~1\Morfeus\Escrit~1\Long__~1"
LaRuta = ""
*#### Establecer el Buffe rpara la llamada a la API
GetLongPath = SPACE(1024)
*#### Llamada a la Api, Retirar los caracteres no deseados y devolver la Ruta
lLen = GetLongPathName(@sShortPath, @GetLongPath, LEN(GetLongPath))
LaRuta = LEFT(GetLongPath, lLen)
WAIT WINDOWS LaRuta

15 de agosto de 2016

Construyendo Objetos MultiCapa en Visual Foxpro

Autor: Jim Booth
Traducción de: Sergio E. Aguirre

El modelo N-Tier

El término N-tier se refiere a los varios niveles de responsabilidad en el diseño de un sistema. La N en N-Tier puede ser cualquier número de 2 en adelante. Un diseño muy común es el modelo 3-Tier. En el modelo 3-tier la aplicación es dividida en 3 capas de responsabilidades distintas, la interface del usuario, la lógica de negocio, y la base de datos. Cada una de estas capas puede ser implementada usando uno o más objetos que se dedican a las responsabilidades de esa capa.

Interface del usuario

La capa interface del usuario contendría todos los aspectos visuales del sistema. Cualquier cosa involucrada en la interacción con el usuario del sistema es manejada por esta capa. Todos los diálogos, cuadros de mensajes, formularios, informes, y otros componentes de interacción con el usuario residirían en la capa interface de usuario de un sistema.

Lógica de Negocio

La capa lógica de negocio tiene la responsabilidad de determinar la forma en que vienen los datos y cómo deben estructurarse para la interface del usuario. También aplica toda regla de validación a los datos provenientes de la interface del usuario antes de mandar estos datos a la base de datos.

La capa lógica de negocio no tiene ningún componente de la interface del usuario en ella como tampoco tiene la responsabilidad de actuar recíprocamente con el usuario. Los problemas percibidos con los datos deben ser comunicados a la capa interface del usuario a través de valores devueltos por métodos y la capa interface del usuario debe mostrar los mensajes al usuario.

Manejo de la Base de Datos

La base de datos es la responsable de manejar el dominio de validación sobre los datos, de actualizar y recuperar los datos en las tablas. Las reglas en la base de datos deben restringirse a sólo aquellas que son de directa aplicación del dominio de validación. "Reglas de Negocio" no son parte de las reglas de la base de datos, en cambio ellas fueron puestas en vigor en la capa lógica de negocio.

Otras Capas

3-Tier no es el único diseño N-Tier. N puede ser cualquier número. Algunas de las cosas que podrían ser consideradas para las capas adicionales son, Interface del Sistema Operativo, Interface de la Red, y los Múltiples Niveles de Capas de Lógica de Negocio.

Por ejemplo, usted puede diseñar un sistema para un banco donde el objeto de lógica de negocio para una Cuenta necesita tener varios formatos diferentes dependiendo de la sección del banco que está usando los datos. En este caso usted puede tener un objeto de lógica de negocio para Cuenta que es genérico para el banco entero, y tiene otros objetos de lógica de negocio que son específicos para las secciones particulares (usando el objeto Cuenta genérico y agregando o restringiendo rasgos basados en los requisitos de la sección).

Ventajas / Desventajas del Diseño N-Tier

Las ventajas del diseño de un sistema N-Tier son múltiples. La siguiente lista muestra algunas de las ventajas.

  1. Usted puede modificar la lógica de negocio sin hacer cambios a la interface del usuario o a la base de datos.
  2. Si lo construyó correctamente, el objeto de lógica de negocio puede ser usado por múltiples interfaces del usuario.
  3. Aisla el conocimiento requerido en cualquier capa dada a esa capa.

Algunas de las desventajas son:

  1. El diseño del sistema es más complejo.
  2. El enlace de datos inherente de Visual FoxPro es indisponible.
  3. La huella de memoria de la aplicación aumenta.

Con estas desventajas, ¿Por qué querría alguien construir un sistema N-Tier? La respuesta es una sola palabra, scalability. El diseño N-Tier puede descascarar a los sistemas sumamente grandes sin compromisos. Por grande estamos refiriéndonos al número de usuarios, el número de diferentes componentes de la interface del usuario, el tamaño de la base de datos, la estructura de la red, y todos los otros problemas de tamaño para una aplicación.

Usando el diseño N-Tier, usted puede diseñar un sistema que pueda manejar múltiples intefaces del usuario divergentes sin tener que volver a escribir la lógica de negocio para cada interface construida. La lógica de negocio puede ser compartida por múltiples interfaces del usuario. Mediante la posibilidad de hacer subclases, las clases de lógica de negocio pueden personalizarse para manejar diferentes servidores de base de datos.

El diseño N-Tier no es apropiado para todos los proyectos, pero cuando se necesita de él es un concepto de diseño sumamente poderoso.

Construyendo Aplicaciones N-Tier en Visual FoxPro

Visual Foxpro puede ser usado para construir cualquiera de las capas comúnes del modelo N-Tier. Visual FoxPro tiene las herramientas para construir interfaces del usuario destacadas. La base de datos nativa de Visual FoxPro es rápida y robusta. Sin embargo, para cada una de estas dos capas hay otras herramientas que también lo hacen bien o mejor. ¿Si la interface necesita imitar a una hoja de cálculo, Excel no sería una mejor opción? Si la base de datos necesita seguridad agregada en la base de datos del servidor, no serían una opción mejor SQL Server o Oracle?

La capa en la que Visual FoxPro tiene ventajas es la capa media, o capa de lógica de negocio, del modelo N-Tier. Debido a la construcción en Lenguaje de Manejo de Datos (DML) de Visual FoxPro, es el primer candidato para manipular datos de un servidor y presentarlos a una interface. También, la posibilidad de crear clases OLE públicas con Visual FoxPro permite interfaces divergentes y bases de datos para usar el mismo objeto de media capa para comunicarse entre sí.

Las Responsabilidades de un Objeto de Lógica de Negocio

Las responsabilidades de un objeto de media capa varían ampliamente. Cosas como la aplicación de la regla de negocio, separación de la interface del usuario y el origen de la base de datos, y proporcionando una sola capa para el acceso a datos para los múltiples servidores de la base de datos está entre las posibles responsabilidades.

Como con muchas otras cosas en el desarrollo orientado a objetos, el diseño del sistema dicta las reales funciones proporcionadas por un objeto de media capa.

Aplicando las reglas de negocio

En cualquier sistema de base de datos hay reglas que controlan que son datos válidos y que son datos inválidos. Estas reglas pueden ser divididas en el dominio de validación y reglas de negocio. Un dominio describe todos los valores posibles que pueden encontrarse en la entidad o atributo al que el dominio aplica. Por ejemplo, el dominio para un campo ciudad puede incluir todos los nombres posibles de ciudades del mundo.

Las reglas de negocio son un subconjunto de un dominio. Las reglas de negocio llevan más allá el límite de valores posibles para ser sólo parte del dominio completo. Con el ejemplo del campo ciudad, quizás nuestra compañía se localiza en Alemania y tiene clientes sólo alemanes. En este caso el dominio para el campo ciudad será todas las ciudades del mundo, pero las reglas de negocio limitarían al campo a sólo ciudades en Alemania.

Mientras que las bases de datos son muy buenas en la aplicación de dominios, ellas pueden ser demasiadas restrictivas si ponen en vigor reglas de negocio. El objeto de lógica de negocio de media capa es un candidato ideal para poner en vigor las reglas de negocio. Visual FoxPro está especialmente preparado para este trabajo porque su motor local de datos permite la creación de diseños de metadata para describir las reglas específicas a ser puestas en vigor. Esto le permite al programador crear reglas de negocios para el manejo de datos que puedan cambiar con el tiempo sin requerir cualquier modificación de código.

Trasladando Datos para la Interface del Usuario

En el diseño 3-Tier la interface del usuario está separada del origen de datos por la capa media, o la capa de la lógica de negocio. Esta separación le permite al programador construir una interface del usuario independiente y capas de almacenamiento de datos. Los perfeccionamientos futuros del sistema pueden incorporar nuevas interfaces del usuario o nuevas tecnologías de almacenamiento de datos sin un cambio que provoque la necesidad de cambiar el otro.

El objeto de media capa realiza el papel de traductor de datos en el formato encontrado en el sistema de almacenamiento de datos a un formato que puede ser usado por la interface del usuario. También traduce los datos desde la interface del usuario a un formato que puede ser guardado por la base de datos.

Con este diseño un cambio en la base de datos requiere sólo que el objeto de media capa sea reforzado, así como un cambio en la interface del usuario también sólo requiere que el objeto de media capa sea reforzado.

Usando Clases de Visual FoxPro para construir un Objeto de Lógica de Negocio

Los ejemplos se pueden descargar aquí.

Exploremos ahora en algo de código para ver una de las muchas maneras que usted puede diseñar un objeto de media capa en Visual FoxPro. La clase de lógica de negocio que nosotros crearemos se va a llamar customer y proporcionará acceso a los datos de ejemplo Fitch Mather que vienen con Visual FoxPro 6.0. La tabla que usaremos es la tabla Stores (Tiendas).

El primer problema con el cual yo traté era qué clase de base usar para crear el objeto customer. Yo escogí usar la clase de base form porque proporciona una sesión de datos privada que protegerá los datos de otras instancias del objeto customer.

A la clase customer yo agregué una propiedad llamada oRDS para usarse como una referencia a un Control de Datos RDS. Yo usé RDS como la metodología de acceso a datos para que la clase customer pueda ser fácilmente modificada para acceder a otros sistemas de base de datos. El control de Datos RDS se crea en el Init de la clase Customer. El código del método Init se describe debajo.

 
* Creo el Control de Datos RDS
This.oRDS = CreateObject("rds.datacontrol")
* Verificamos si la creación tuvo éxito 
If Type("This.oRDS") <> "O" 
   * Si es no devuelvo .F. 
   Return .F. 
Else 
   * Si tuvo éxito fijo algunas propiedades del datacontrol
   With This.oRDS 
      * Establezco el nombre del origen de Datos
      .Connect = "dsn=dsnFitchMather" 
      * Prepare la declaración SQL para ejecutarla
      .SQL = "Select * from stores" 
      * Fijo la ejecución síncrona
      .ExecuteOptions = adcExecSync 
      * Fijo el saque en el fondo 
      .FetchOptions = adcFetchBackground 
      * Ejecuto la consulta 
      .Refresh 
   EndWith 
EndIf 

Los comentarios en el código anterior son autoexplicativos. Una vez que el objeto customer existe tiene el control de Datos RDS dentro de él y el Control de Datos RDS está sacando los datos.

NOTA:

Las constantes referidas en el código anterior se toman de un archivo llamado adcvbs.h. Los contenidos de este se listan debajo.

*-------------------------------------------------------------------- 
* Microsoft ADC 
* (c) 1997 Microsoft Corporation. All Rights Reserved.
* ADO constants include file for VBScript
*-------------------------------------------------------------------- 
*---- enum Values ---- 
#Define adcExecSync 1 
#Define adcExecAsync 2 
*---- enum Values ---- 
#Define adcFetchUpFront 1 
#Define adcFetchBackground 2 
#Define adcFetchAsync 3 
*---- enum Values ---- 
#Define adcReadyStateLoaded 2 
#Define adcReadyStateInteractive 3 
#Define adcReadyStateComplete 4 

A esta clase yo he agregado varios métodos que se listan en la siguiente tabla.

Método

Propósito

GetValue

Usado para obtener el valor de un campo

SetValue

Usado para establecer el valor de un campo

MoveFirst

Mueve al primer registro en el RecordSet

MoveLast

Mueve al último registro

MoveNext

Mueve al próximo registro

MovePrev

Mueve al registro anterior

Requery

Refresca el Control de Datos mediante la re-ejecución de la consulta SQL. El método requery está para hacer que la sintaxis del objeto customer sea similar a la sintaxis nativa de VFP nativa para el requerying de una vista.

RevertChanges

Descarta los cambios pendientes de los datos

SaveChanges

Graba los cambios pendientes de los datos

Las siguientes secciones presentarán el código que está en estos métodos.

GetValue

LPARAMETERS PcField
* Verifico por un parámetro válido 
IF NOT EMPTY(pcField) AND VARTYPE(pcField) = "C" 
   * Verifico si es un nombre de campo válido para este objeto
   IF LOWER(pcField) $ "store_name~store_add1~store_addr2~store_addr3~" + ;
      "store_city~store_id~store_desc~store_phone1~" + ; 
      "store_state~store_type~store_zip" 
      * Demanda de un campo válido para este objeto, así que devuelvo el valor 
      RETURN THIS.oRDS.Recordset.Fields(pcField).Value 
   ENDIF 
ENDIF 
* Demanda de un campo inválida 
RETURN .NULL. 

SetValue

LPARAMETERS PcField, pxValue
IF NOT EMPTY(pcField) AND VARTYPE (pcField) = "C" 
   IF LOWER(pcField) $ "store_name~store_add1~store_addr2~store_addr3~" + ; 
      "store_city~store_id~store_desc~store_phone1~" + ; 
      "store_state~store_type~store_zip" 
      IF VarType(pxValue) = "C" 
         pxValue = ALLTRIM(pxValue) 
      ENDIF 
      THIS.oRDS.Recordset.Fields(pcField).Value = pxValue 
      RETURN .T. 
   ENDIF 
ENDIF 
RETURN .F.

MoveFirst

ThisForm.oRDS.RecordSet.MoveFirst
RETURN 1 

MoveLast

ThisForm.oRDS.RecordSet.MoveLast
RETURN 1

MoveNext

LOCAL LnRet
lnRet = 1 
ThisForm.oRDS.RecordSet.MoveNext 
If ThisForm.oRDS.RecordSet.Eof 
   lnRet = -1 
   ThisForm.oRDS.RecordSet.MoveLast 
EndIf 
RETURN lnRet 

MovePrev

LOCAL LnRet
lnRet = 1 
ThisForm.oRDS.RecordSet.MovePrevious 
If ThisForm.oRDS.RecordSet.Bof 
   lnRet = -1 
   ThisForm.oRDS.RecordSet.MoveFirst 
EndIf 
RETURN lnRet

Requery

ThisForm.oRDS.Refresh

RevertChanges

THISFORM.Requery()

SaveChanges

ThisForm.oRDS.SubmitChanges()
THIS.Requery()

Usted puede preguntarse por qué yo he creado todos estos métodos para hacer cosas que podría haber hecho refiriéndome directamente al Control de Datos RDS. La respuesta es que esto proporciona una interface de desarrollo para el objeto de negocio que es independiente de la naturaleza del objeto de datos. Yo puedo hacer subclases de esta clase y escribir código que con datos locales de VFP, o ADO en lugar de RDS, o ODBC a través de vistas remotas. Ninguno de éstas modificaciones requeriría que cualquier código, en el nivel de UI, sea cambiado en absoluto.

Usando el Objeto de Negocio

El proyecto también incluye un formulario de VFP que demanda esta clase de negocio. El nombre del formulario es Customer.scx. Examinemos el código de este formulario que usa la clase de negocio.

En este formulario se agrega una propiedad llamada oBusObj. En el evento Load del formulario el código será el siguiente.

THISFORM.oBusObj = NewObject("Customer")

Esto crea una instancia del objeto customer y almacena una referencia a él en la propiedad oBusObj del formulario. En el Refresh del textbox número de tienda este es el código:

THIS.Value = THISFORM.oBusObj.GetValue("store_id")

Este código llama al GetValue del objeto de negocio y fija la propiedad Value del textbox para ser el valor devuelto por el método. El evento Valid para el mismo textbox es:

THISFORM.oBusObj.SetValue("store_id",THIS.Value)

Que escribe el Valor devuelto a la fuente de datos del objeto de negocio.

El evento Click para el botón Top es:

THISFORM.oBusObj.MoveFirst()
THISFORM.Refresh()

¿Está empezando a ver un patrón aquí? ¿Puede apreciar lo fácil que es usar este objeto de negocio para manejar el acceso de datos? El otro código en el formulario es similar salvo que se llaman métodos del objeto de negocio.

Creando un servidor ActiveX desde las clases de Visual FoxPro

Así que, ¿Cuál es el gran trato? ¿Por qué es mejor usar un objeto separado para manejar el acceso a datos cuándo el formulario tiene que un entorno de datos maravilloso y controles que pueden enlazar directamente los datos?

La respuesta a estas mentiras es la palabra scalability. Scalability es la habilidad de un sistema de crecer incluir nuevos rasgos con el tiempo, grandes volúmenes de datos, interfaces del usuario adicionales, y otros perfeccionamientos. Si usted demanda el entorno de datos de un formulario de VFP por acceder a los datos, entonces usted se limita a usar VFP para construir la interface del usuario o usted necesitará crear las mismas capacidades de acceso a datos en alguna otra herramienta.

¿Qué pasa si las mismas necesidades de acceso a datos estén disponibles a VFP y a Microsoft Excel? La respuesta es hacer a la clase customer como una clase OLE Pública y construir un COM DLL con ella incluida. Para hacer una clase OLE Pública, abra el diseñador de clases y luego elija Información de clase... en el menú Clase. Verifique que esté marcada la casilla OLE público.

Para construir el COM DLL abra el proyecto y escoja la opción Información del proyecto... del menú Proyecto, seleccione la etiqueta Servidores y establezca sus opciones de la clase (estas opciones están bastante bien documentadas en el archivo de ayuda). Guarde esas opciones y entonces escoja Generar y seleccione la opción Servidor COM (dll) y pulse el botón Aceptar. Esto va a generar el COM DLL con su clase COM servidor en él y registrará la DLL en su máquina. Para otros para usar la clase DLL necesitará ser instalada y registrada en sus máquinas, esto puede hacerse como parte del proceso de Instalación para su aplicación.

Una vez que usted ha hecho esto puede modificar sus formularios de VFP para que usen la clase COM. Simplemente cambie el evento Load para que sea:

THISFORM.oBusObj = NewObject("BusObj.Customer")

Donde BusObj es el nombre del archivo DLL que usted creó.

Usando Múltiples Interfaces del Usuario en el Diseño N-Tier

La ventaja de este diseño es que el mismo objeto de negocio puede ser usado por múltiples interfaces del usuario diferentes permitiendo una definición de clase para controlar el acceso a datos para cada UI que su sistema usa. Aunque su aplicación sólo pueda limitarse a formularios de VFP al principio, siguiendo este diseño N-Tier le permitirá, de manera más fácil, agregar otras interfaces al sistema en el futuro (haciendo el sistema escalable).

Usando Microsoft Excel para la Interface del Usuario

Aquí hay un ejemplo de una macro de Microsoft Excel que usa la misma clase de negocio para llenar con datos una hoja de cálculo.

Option Explicit
Public Dummy As Variant
Public oCustomer As Object
Sub nTier()
   ' nTier Macro
   '
   Dim lnRet As Integer
   Set oCustomer = CreateObject("BusObj.Customer")
   ActiveSheet.Cells(1, 1) = "Store ID"
   ActiveSheet.Cells(1, 2) = "Store Name"
   ActiveSheet.Cells(1, 3) = "Store City"
   lnRet = Refresh()
   ' frmRefresh.Show
End Sub
Public Function Refresh()
   ' nTier Macro
   ' Refreshes the contents of the business logic object and the sheet.
   Dim lnRet As Integer
   Dim lnRow As Integer
   lnRet = oCustomer.MoveFirst()
   lnRow = 2
   Do While lnRet > 0
      ActiveSheet.Cells(lnRow, 1) = oCustomer.GetValue("store_id")
      ActiveSheet.Cells(lnRow, 2) = oCustomer.GetValue("store_name")
      ActiveSheet.Cells(lnRow, 3) = oCustomer.GetValue("store_city")
      lnRet = oCustomer.MoveNext()
      lnRow = lnRow + 1
   Loop
   Refresh = 1
End Function

Esta macro creará una instancia de la clase customer y entonces llenará las filas y columnas de la hoja con datos del objeto de negocio.

Resumen

Hay muchas charlas alrededor del diseño de sistemas N-Tier. Algunas personas son fuertes defensoras de usar N-Tier para todo, en cambio otras personas sienten que el N-Tier es excesivo en muchos lugares. Mi opinión es que algo que yo pueda hacer para mejorar con el tiempo un sistema para mi cliente es bien esfuerzo bien hecho.

El diseño N-Tier me da la capacidad de manejar datos a través del uso de clases de las que se pueden hacer subclases y pueden ser especializadas dentro de VFP y al mismo tiempo pueden hacer que esas clases estén disponibles a otras herramientas de desarrollo que manteniendo el lugar solo modificando el acceso de datos. Esta sola situación es uno de los mayores beneficios logrados a través del diseño N-Tier. Si el cliente cambia el servidor de la base de datos a otra, hay sólo un lugar para hacer los cambios necesarios y todas las interfaces del usuario se actualizarán.

Envíe un email a Jim Booth con preguntas o comentarios sobre esta información.

10 de agosto de 2016

Operador XOR en VFP

En VFP no existe el operador XOR, podemos utilizar el siguiente truco para así poder usarlo.
Definimos XOR como la desigualdad #

#DEFINE XOR #
? "Tabla de verdad del operador XOR"
? .F. XOR .F.
? .F. XOR .T.
? .T. XOR .F.
? .T. XOR .T. 

Mostramos para mas ayuda la tabla de verdad de los opradores AND, OR y XOR

VALORESRESULTADOS APLICANDO:
ANDORXOR
FALSEFALSEFALSEFALSEFALSE
FALSETRUEFALSETRUETRUE
TRUEFALSEFALSETRUETRUE
TRUETRUETRUETRUEFALSE

3 de agosto de 2016

Obtener la dirección IP local con Winsock

************************************************************
* Clase: GET_IPADDRESS
*
 Devuelve la dirección IP local
*
* Parametros: ninguno
*
* Ejemplos:  ipdir=get_ipaddress()
*
* Retorno: Dirección IP o cadena vacia si no está instalado el WinSock
*
* Nota: Adaptado de John Harvey
*
************************************************************
LOCAL lcret
IPSocket = CreateObject("MSWinsock.Winsock")
IF TYPE('IPSocket')='O'
 lcret = IPSocket.LocalIP
ELSE
 MESSAGEBOX("Winsock no está instalado!")
 lcret = ""
ENDIF
RETURN lcret
************************************************************
Pablo Roca