23 de diciembre de 2004

Obtener la version con que fue compilado un EXE de VFP

Deseas saber con que version fue creado un .EXE, a continuacion una funcion creada por Rick Bean...

El siguiente codigo fue obtenido de los newsgroups de VFP en ingles:

**************************************************
* Subject. Re: Identifying the versions of VFP used to build an EXE or DLL
* Sender: Rick Bean rgbean@unrealmelange-inc.com
* Date: 04/11/2004 8:01
* newsgroup: microsoft.public.fox.programmer.exchange
**************************************************

Hay que hacer notar que funcionara con ejecutables desde FoxPro para Windows, hasta el VFP9b.

**************************************************
* Funcion: GetFoxEXEVersion
* Obtiene la version en que fue compilado un EXE
* Autor: Rick Bean rgbean@unrealmelange-inc.com
* Ejemplo: ?GetFoxEXEVersion(GETFILE("EXE"))
**************************************************

* GetFoxEXEVersion.prg
FUNCTION GetFoxEXEVersion
LPARAMETERS p_cEXEName
DIMENSION VersionInfo[8,3]
VersionInfo[1,1] = "FPW 2.5"
VersionInfo[2,1] = "FPW 2.6"
VersionInfo[3,1] = "VFP 3.0"
VersionInfo[4,1] = "VFP 5.0"
VersionInfo[5,1] = "VFP 6.0"
VersionInfo[6,1] = "VFP 7.0"
VersionInfo[7,1] = "VFP 8.0"
VersionInfo[8,1] = "VFP 9.0"
VersionInfo[1,2] = "foxw"
VersionInfo[2,2] = "foxw"
VersionInfo[3,2] = "VisualFoxProRuntime.3"
VersionInfo[4,2] = "VisualFoxProRuntime.5"
VersionInfo[5,2] = "VisualFoxProRuntime.6"
VersionInfo[6,2] = "VisualFoxProRuntime.7"
VersionInfo[7,2] = "VisualFoxProRuntime.8"
VersionInfo[8,2] = "VisualFoxProRuntime.9"
VersionInfo[1,3] = "00D1"
VersionInfo[2,3] = "0111"
VersionInfo[3,3] = ""
VersionInfo[4,3] = "3228"
VersionInfo[5,3] = "1418"
VersionInfo[6,3] = "162C"
VersionInfo[7,3] = "1638"
VersionInfo[8,3] = "10EC" && beta 1
LOCAL lnii, lcVersion, lnHandle, lcKeyName
lnHandle = FOPEN(p_cEXEName, 0)
IF lnHandle < 0
 RETURN "Unable to Open file"
ENDIF

lcVersion = "(unknown)"
FOR lnii = 1 TO 8
 IF !EMPTY(VersionInfo[lnii,3])
  = FSEEK(lnHandle, EVALUATE("0x"+VersionInfo[lnii,3]))
  lcKeyName = VersionInfo[lnii,2]
  IF FGETS(lnHandle, LEN(lcKeyName)) == lcKeyName
     lcVersion = VersionInfo[lnii, 1]
     EXIT
  ENDIF
 ENDIF
ENDFOR
=FCLOSE(lnHandle)

RETURN lcVersion

Espero les sea de utilidad.

Espartaco Palma Martinez

17 de diciembre de 2004

Como se mueve el ratón


Originalmente publicado en FoxTALK Febrero de 1998
Traducción de Ariel Gimenez


Como se mueve el ratón
Jim Booth
Si tu aplicación necesita contemplar las acciones del ratón, puedes tanto llamar a un exterminador o usar los eventos que Visual FoxPro nos dá para capturar las acciones del mouse y sus botones. Desde que mis usuarios se enojaron al ver ese tipo del traje metalico destruyendo sus ratones, prefiero usar los eventos enVisual FoxPro.
Allí hay un número de eventos que pueden informarnos de las actividades del ratón. MouseMove, MouseDown, MouseUp, Click, RightClick, DoubleClick, MiddleClick, y el MouseWheel son los eventos que nos interesan.
Echemosle una mirada al MouseMove primero.  El archivo de ayuda nos dice que este evento se dispara cuando se mueve el ratón sobre un control.   Esto es correcto, y debido a que constantemente se dispara mientras el ratón se esta moviendo sobre el control puede ser un problema para la performance, si no es utilizado correctamente.  Recuerda cuando escribes codigo en el MouseMove que este correra muy frecuentemente y por consiguiente este debe ser corto y conciso.
El MouseMove recibe cuatro(4) parámetros.   Estos son nButton, nShift, nXCoord, y nYCoord respectivamente.  Estos parámetros nos cuentan sobre la posición del puntero del ratón, el estado de los botones del ratón, y el estado de las teclas shift, ctrl y alt .  Sus valores son:
El parámetro nButton posee un valor entre 0 y 7 y se refiere al estado de los botones del ratón.   El valor 0 significa que no hay botones presionados mientras que los valores de 1, 2, y 4 cada uno se refiere a que los botones izquierdo, derecho, y medio se encuentran presionados respectivamente. Los otros valores  (3,5,6,7) nos informa de las combinaciones de botones que estan presionados.   El parámetro nButton posee un valor que es la suma de los valores de los botones presionados, así que si el boton izquierdo y derecho están presionados entonces nButton será igual a 3, o la suma de 1 (botón izquierdo) y 2 (botón derecho).
El parámetro nShift nos dá una información similar sobre las teclas Shift, CTRL, y Alt .   Los valores para este parámetro son manejados similarmente a el parámetro nButton en el cual Shift es 1, CTRL es 2, y Alt es 4.   Ninguna es 0 y si mas de una tecla de estas esta presionada entonces nShift será la suma de los valores de las mismas.
El parámetro nXCoord y nYCoord nos informan de la posición horizontal y vertical del ratón respectivamente.   Los valores de este parámetro son tomados con respecto al formulario y no del control, así que cuando el mouse ingresa a la parte de arriba del control el nYCoord NO es 0 o 1 sinó el número de pixel desde el formulario mismo.
El formulario de ejemplo Mice.scx muestra el uso del evento MouseMove de un botón para actualizar algunos otros controles.   los otros controles nos van mostrando el estado de las cosas mientras el ratón se mueve sobre el command button.   Un extracto del código del MouseMovecode del botón esta listado abajo, este extracto es el código que determina si el puntero del ratón esta entrando, saliendo, o moviendose dentro del command button.  Hace esto chequeando el nXCoord del puntero del ratón para ver si este cae dentro de los 3-pixels del borde  arriba, abajo, izquierda, o derecha del limite del botón. Si el puntero se encuentra en este perímetro entonces chequea si el ratón se encontraba previamente sobre el botón o no(en el ejemplo estamos usando el valor corriente del option button group, pero en código real esto debería ser manejado con una propiedad del botón mismo) y entonces setear el valor entrante o saliente del OptionButton group en el formulario para reflejar esto.   Hay también código en el MouseMove del formulario que setea el valor del  OptionButton group en 0 indicando que el ratón no se encuentra sobre el boton.
LPARAMETERS nButton, nShift, nXCoord, nYCoord
WITH THISFORM
       IF ((nXCoord >= THIS.Left AND nXCoord <= THIS.Left+3) OR ;
          (nXCoord <= THIS.Left + THIS.Width AND ;
           nXCoord >= THIS.Left + THIS.Width-3)) OR ;
          ((nYCoord >= THIS.Top AND nYCoord <= THIS.Top+3) OR ;
          (nYCoord <= THIS.Top + THIS.Height AND ;
           nYCoord >= THIS.Top + THIS.Height-3))
                 DO CASE
                     CASE .optEntering.Value = 0
                            * estamos entrando
                            .optEntering.Value = 1
                     CASE .optEntering.Value = 2
                            * estamos saliendo
                            .optEntering.Value = 3
              ENDCASE
       ELSE
* estamos sobre el control
              .optEntering.Value = 2
       ENDIF
* mas código aqui
ENDWITH
El formulario de ejemplo está disponible para los suscriptores de FoxTALK en el area de downloads en http://www.pinnaclepublishing.com/ft.  El formulario de ejemplo muestra como leer el estado de los botones del ratón, las teclas shift/ctrl/alt, y las coordenadas x e y del ratón, y también el manejo de la entrada y salida del control.  si usted no es suscriptor puede suscribirse desde el mismo sitio web de FoxTALK.
 

Jim Booth

9 de diciembre de 2004

¿Cómo y cuándo usar subconsultas...? SELECT-SQL

Alguna vez se ha preguntado como sacar esa consulta SQL que le han pedido?, intentas proyectar tablas con FULL JOIN, INNER JOIN, LEFT JOIN y todavia no se obtienen los resultados deseados...

A veces hay que recurrir a las subconsultas para llegar a nuestro objetivo... Quizás es típico el estar buscando las formas de ejecutar consultas SELECT-SQL para obtener un conjunto de datos específico, esto porque ya estás cansado de crear cursores hechos con CREATE CURSOR y rellenados mediante un ciclo SCAN ... ENDSCAN (ok, podrías hacerlo con DO WHILE NOT EOF(), pero eso ya está pasado de moda, además de que resulta ser poco eficiente debido a que no utilizarás a su máximo la optimización RushMore, una de las ventajas del uso de Visual FoxPro). Haz consultado con algunos de tus colegas y te responden que efectivamente, debe haber alguna manera de hacerlo por medio de sentencias SQL. Y tu sexto sentido casi te lo puede gritar!!

A continuación expondremos un caso típico en los cuales será necesario hacer unos cuantos "trucos" para obtener el tan ansiado cursor que no nos deja en paz:

La gerencia te pide que expongas en un reporte un resumen de las compras y ventas de los artículos en el último mes, donde compares lado a lado, cuantos se vendieron, cuantos se compraron por artículo, quedando quizas algo por el estilo:

ID Articulo Descripcion Compras Ventas
001 Camisa XXX XXX
002 Pantalon XXX XXX
003 Blusa XXX XXX

Parece algo sencillo y no muy difícil de realizar, pero es tipico que en el diseño de tu base de datos hayas dejado por separado una entidad para compras y una entidad para ventas, por lo que tendriamos los siguientes entidades:



Lo primero que se nos podría ocurrir es una consulta donde proyectaramos "todos" vs "todos" para obtener conjunto de datos deseados:

SELECT Ventas.IDArticulo, SUM(compras.Cantidad) as SumaCompras,;
    SUM(ventas.cantidad) AS SumaVentas ;
  FROM Compras ;
    FULL JOIN Ventas ON Ventas.IdArticulo = Compras.IdArticulo ;
  GROUP BY Ventas.idarticulo ;  
INTO CURSOR cResumen

A primera vista parece buena la idea, sumamos todas las cantidades de compras, todas las cantidades de ventas de una mezcla de todos los registros, no? Pero sabras que pasa?: No funciona, ya que obtendremos datos que nada tienen que ver, primeramente porque se estan cualificando las tuplas de la tabla ventas contra las tuplas de la tabla compras, pero nótese que hay algunos IDs que están en una, pero que no están en la otra, dandonos el siguiente resultado:



Podras comentar, "Ya casi", solo me faltó el codigo 002 que es de la tabla compras que no estaba en la tabla ventas, hmmm si, en efecto es asi, puedes seguir intentando cambiar las clausulas por LEFT JOIN, RIGTH JOIN, cambiar Ventas.Articulo = Compras.IdArticulo por Compras.IdArticulo = Ventas.Articulo en lo correspondiente a la cláusula ON (de los operadores de proyeccion), pero obtendrás diferentes resultados que seguirán sin ser los que esperabas.

Inclusive, puedes llegar a pensar que tal vez el operador de igualdad sale sobrando, e intentas una más para ver si funciona:

SELECT ventas.idArticulo,;
   SUM(ventas.cantidad) AS TotalVentas,;
   SUM(compras.cantidad) AS TotalCompras ;
  FROM ventas,compras ;
  GROUP BY ventas.idArticulo ;
  INTO CURSOR cReporte

Y tenemos el siguiente resultado:



Que sucedió?, lamentablemente al hacerlo de esta forma se está utilizando implicitamente un operador de igualdad entre las entidades (INNER JOIN), y eso sin tomar en cuenta que la suma no son lo que deberia tener, por lo que tampoco obtenemos lo que queremos.

Entonces? Qué es lo que debemos hacer?, la solución es sencilla, el uso de subconsultas. Resulta ser que el algebra de conjunto (que es la teoría que sustenta a la práctica de el lenguaje SQL) tiene un pequeño "truco" para normalizar este pequeño desperfecto en lo que parece no tener solución. Esto se le llaman subconsultas (mas adelante veremos que otros nombres se les conoce).

En el parrafo anterior hice mención a un tema que es base de esto, la normalización, en vez de intentar proyectar las columnas que serán calculadas (en nuestro ejemplo se sumaron, pero bien pudieron haber sido contadas) para llegar al resultado inmediato, debemos crear un conjunto de datos intermedio, el cual, nos servirá para ahora si proyectarlo correctamente:

SELECT idArticulo, 000000 as nCompras, Cantidad as nVentas ;
       FROM Ventas ;
   UNION ;
SELECT idArticulo, Cantidad as nCompras , 000000 as nVentas ;
       FROM Compras ;
 INTO CURSOR cResumen

Con el query anterior obtendremos un conjunto de datos normalizados como el que sigue:



Ya se va viendo mejor?, claro!, ahora a éste conjunto de datos sólo le faltaría hacer una agrupación y suma, para que quede como lo deseamos:

SELECT idArticulo, SUM(nCompras) as TotalCompras ,;
         SUM(nVentas) as TotalVentas ;
  FROM cResumen ;
  GROUP BY idArticulo ;
  INTO CURSOR cReporte

Quedando como sigue:



Ahora si, llegamos a los resultados deseados (puedes hacer la suma manual, no me he equivocado :-), como comentaba en el inicio del artículo a veces es *NECESARIO*. Cabe destacar que en las consultas he obviado las clásulas WHERE, ya que es demasiado obvio que siempre deberán incluirse en cada una de la consultas que hemos unido (con la claúsula UNION).

Aprovecho este momento de tu atención para demostrar la manera en que también podrá ser realizado con la VFP9 (la próxima versión del producto), en la que se puede simplificar aún más esta sentencia SQL:

SELECT codigo,;
    SUM(cResumen.nCompras) AS TotalCompras ,;
    SUM(cResumen.nVentas ) AS TotalVentas ;
  FROM (SELECT codigo, ;
               CAST(0 as Int) AS nCompras, Cantidas as nVentas ;
            FROM Ventas ;
          UNION ;
        SELECT Codigo,;
               Cantidad as nCompras, CAST(0 as Int) AS nVentas ;
            FROM Compras ) cResumen ;
  GROUP BY cResumen.Codigo ;
  INTO CURSOR cReporte

Aquí se hará uso de lo que se le denomina sentencias anidadas, en donde tenemos varias partes clave.

En primer lugar se puede apreciar que se está realizando una sentencia anidad en al utilizar como cursor de procedencia ... otra consulta!! FROM ( SELECT ... ), esto nos dá la ventaja de poder anidar tantas consultas sean necesarias.

Como segundo punto a favor estamos utilizando una de las nuevas funciones: CAST(), con ella prescindiremos de los trucos que hemos estado usando para "forzar" que las columnas calculadas de VFP tengan un tipo de datos, ancho o precisión que se requiera, en vez de tener que utilizar por ejemplo 000000 para forzar que fuera un entero de ancho 6, o usar $0 para que sea de tipo Moneda, y un sin fin de etceteras.

Con la combinación de ambas, obtenemos una consulta mas compacta, te podrán preguntar ¿En qué nos beneficia eso?, en que dicha consulta, si en dado momento desea pasarse a un servidor de base de datos (por ejemplo MS-SQLServer), la transición sea más ligera, ya que desde hace mucho tiempo estos manejan consultas anidadas y funciones de conversión explícita.

Quizás no debemos de dejar pasar el hecho que a veces puede ser contraproducente el tener todo en un una sola consulta, ya que todo dependerá de cómo estén formados tus datos, asi por ejemplo, pudiera ser necesario que separes en varias consultas para darle mayor interactividad a tu aplicación, por ejemplo:

WAIT WINDOW "Buscando Ventas en el periodo... Espere" NOWAIT

SELECT idArticulo, 000000 as nCompras, Cantidad as nVentas ;
   FROM Ventas ; 
   WHERE BETWEEN(Fecha, ldInicio, ldFinal) ;
   INTO CURSOR cVentas ;

WAIT WINDOW "Buscando Compras en el periodo... Espere" NOWAIT

SELECT idArticulo,  Cantidad as nVentas,000000 as nCompras ;
   FROM Compras ; 
   WHERE BETWEEN(Fecha, ldInicio, ldFinal) ;
   INTO CURSOR cCompras ;

WAIT WINDOW "Realizando resumen de Compra ventas... Espere" NOWAIT

SELECT * FROM cCompras ;
  UNION ;
SELECT * FROM cVentas ;
 INTO CURSOR cResumen

Asi pues, entre más pasos intermedios dejes, puedes enviar más mensajes a tu usuario indicándo qué algo se está haciendo, ya que en cuanto el número de datos empiece a crecer puede ser una posibilidad a que se vaya tardando cada vez más, por ejemplo, si lo pruebas con 1000 datos... será rapidísimo, pero en cuanto llegues a los millones de registros, se tardará, y muchos usuarios sienten que dos o tres segundos es un mundo, y mientras se tarda los puedes animar un poquito, así no se desesperarán y empiecen a "tronar" tu aplicación porque "ya no respondía" ... El cuánto las separes o unas te lo dejo a tu criterio, ya que siempre dependerá de tu caso específico.

Sea cual sea la solución tomada, debemos resumir lo siguiente: Es un error común el pensar que todo se puede hacer con una sola consulta SELECT-SQL, a veces hay que utilizar subconsultas (o consultas anidadas), y no solo proyecciones directas.

Espero que este pequeño tutorial les sea de utilidad.

Espartaco Palma Martínez

6 de diciembre de 2004

Reduciendo el tiempo de desarrollo


Usando la Orientación a Objetos para Reducir el Tiempo de Desarrollo
Por Jim Booth
Traducción de Sergio E. Aguirre

Introducción
Visual FoxPro es Orientado a Objetos. ¿Qué es lo que hace eso por mí? ¿Cómo la orientación de objetos puede hacer mi vida más fácil? Todos sabemos que hay una curva de aprendizaje a subir para entender qué nos han dicho sobre objetos y que hay una recompensa al subir esa curva. Este artículo investigará uno de las recompensas, una reducción para nuestro tiempo de desarrollo.

Tiempo de Desarrollo
El tiempo de desarrollo es siempre muy importante y los pasos que tomamos para reducir este tiempo son muy valiosos. La orientación a objetos nos promete que nuestro trabajo será reusable y que cada proyecto que nosotros desarrollamos tomará menos tiempo que el anterior. ¿Cómo nos cobramos esa promesa?
Como es el caso en todas las situaciones, la solución a un problema nunca se encuentra hasta que el problema sea entendido y definido claramente. El tiempo de desarrollo puesto bajo un microscopio nos muestra que está dividido en varias tareas diferentes. Las mayores son el análisis, diseño, implementación, prueba, y entrenamiento del usuario. ¿Cuáles son estas tareas y cómo influyen éstas en el tiempo global de desarrollo?

Análisis
Todo el desarrollo de la aplicación empieza con el análisis del problema comercial a ser resuelto. Un análisis completo es crítico para una solución exitosa. Nos dicen que en el desarrollo orientado a objetos, la fase de análisis ocupará un porcentaje más grande del tiempo de desarrollo total que en las prácticas de programación estructuradas del pasado. ¿Qué podemos hacer para reducir el tiempo requerido para esta fase de un proyecto?
Buscando patrones en problemas comerciales reduciremos bastante el tiempo destinado al análisis. Catalogando estos patrones cuando los identifiquemos nos permitirán descubrirlos más fácilmente en el futuro. Con un catálogo de patrones en nuestras manos podremos reconocer rápidamente similitudes a los problemas comerciales anteriores y rápidamente podremos registrar los requerimientos.
Hay un peligro en cualquier esfuerzo para reducir el tiempo de análisis es el de producir un análisis incompleto. Cuando cortamos esquinas sobre el análisis de un problema de negocio nosotros potencialmente abrimos el proyecto a una cantidad de problemas que van a consumir tiempo. Es posible que no se descubran requisitos perdidos hasta que el proyecto esté bien encaminado para su finalización y que esos requisitos no descubiertos nos obliguen a abandonar un grupo grande de trabajo debido a los cambios en el diseño.
Si el problema de negocio no es entendido claramente por los diseñadores y programadores de una aplicación, es probable que la aplicación no satisfaga las necesidades del usuario. Las aplicaciones que no reúnan los requisitos de los usuarios son abandonadas o escritas de vuelta. Es asustadiza cómo verdadera la vieja frase: "Nunca hay bastante tiempo para hacerlo correctamente, pero siempre hay bastante tiempo para hacerlo encima."
Aparte de reconocer, usar, y catalogar los patrones vistos durante el análisis, ésta es una de las dos áreas del proceso de desarrollo que no debe abreviarse.

Diseño
El diseño es la abstracción de una solución, en otras palabras es la descripción general de la solución a un problema sin los detalles. Así como pueden categorizarse problemas de negocio en patrones que se repiten, los diseños también exhiben patrones. Patrones vistos en la fase del análisis pueden ser trazados en el diseño. Catalogando y reusando patrones de diseño podemos reducir el tiempo requerido para crear el diseño de la solución.
La fase de diseño es el segundo paso de desarrollo que no debe abreviarse. Es sorprendente qué fácil y rápida pueden lograrse las metas cuando esta fase se define claramente. La fase de diseño es la planificación de la solución, sin un buen diseño podemos seguir un gran número de puntos muertos. Estos puntos muertos consumirán mucho más tiempo que el desarrollo del propio diseño en sí.
Comparando la construcción de una aplicación con una vacaciones en familia. ¿Si, en el momento de las vacaciones, usted metió a su familia dentro del automóvil y empezó a manejar para cruzar el país y usted no tenía ningún mapa y ninguna ruta planeada, piensa usted que en esas condiciones alcanzará su destino? ¿Cuántos malos giros podría tomar usted en el viaje? ¿Cuánto tiempo le tomaría estar al otro lado del país? Construir una aplicación sin un buen diseño es algo muy parecido.


Implementación
La implementación es la ejecución del plan de diseño. Esta parte es donde nosotros escribimos el código que hace realmente el trabajo. Este artículo se enfoca en técnicas para reducir el tiempo de la fase de implementación del desarrollo de una aplicación.
Empezando con un legítimo framework pueden reducir el codificado de la aplicación quitando la necesidad de tratar con las actividades rutinarias de las interacciones del componente.
Reusar código previamente exitoso ha sido una meta para programadores desde hace mucho tiempo. La orientación a objetos promete, de nuevo, permitir la reusabilidad de nuestro código, a pesar de que la orientación a objetos nos permite reusar no más que código. Nosotros podemos reusar cosas reales, widgets. Estos widgets pueden ser tan simples como un cuadro de texto que maneja entradas de fecha, o tan complejo como un formulario que maneja la administración de los datos. Un framework es un widget reusable muy complejo comprendido por muchos objetos que proporcionan un servicio a la aplicación.
Usted puede reconocer widgets útiles durante el desarrollo de un proyecto. Una vez que usted se encuentra haciendo la misma cosa por segunda vez, deténgase y pregúntese si esto podría ser manejado por un widget reusable.

Prueba
Las pruebas pueden extenderse a lo largo de un proyecto y también después de su finalización. Probamos cosas cuando las construimos y entonces de nuevo las probamos cuando el proyecto está completo para cerciorarnos que ellas trabajan bien juntas. Cuando estamos reusando widgets previamente probados, ahorramos un poco de tiempo porque estos elementos reusables ya se han probado individualmente, quedándonos solamente por probarlos en unión con los demás elementos.


Formación
La formación del usuario es uno de los aspectos pasados por alto a menudo, y centro del costo, del desarrollo de la aplicación. Una nueva aplicación no hace nada bueno para el negocio hasta que los usuarios puedan hacerla trabajar. Mediante la capitalización sobre componentes reusables promovemos una interface consistente para nuestras aplicaciones. Esto, a su vez, reduce el tiempo de formación requerido para que los usuarios se sientan cómodos con el nuevo sistema porque ellos ya están familiarizados con la interface del usuario.

Los Principios de la Reusabilidad Promovida en la POO
El desarrollo de sistemas orientados a objetos tiene un conjunto de principios y reglas que prescriben la manera en que deben hacerse las cosas. Se oye de todos a menudo que la teoría es buena pero el pragmatismo se apodera del trabajo hecho. Esta es una actitud infortunada para los desarrolladores de software que usan herramientas orientadas a objetos. No se fundaron los principios de orientación a objetos en algún cuarto de la parte trasera de una universidad, ellos se descubrieron durante el desarrollo de un proyecto real a través del análisis de cosas que no funcionaban.
Estos principios y reglas son pautas hacia el éxito y ellos necesitan ser respetados y entendidos. Siguiendo las reglas, éstas nos protegen de cometer errores que otros ya han cometido antes. Es verdad que cualquier regla puede romperse, sin embargo también es verdad que antes de romper una regla uno debe:
  1. Saber que la regla existe.
  2. Saber lo que es la regla.
  3. Saber por qué las reglas existen
  4. Saber los problemas potenciales relacionados con el rompimiento de la regla.



Si usted sabe todas estas cosas y todavía escoge romper la regla, prosiga y rómpala. ¿Qué tiene de malo todo esto? Significa que es necesario saber lo que son las reglas y principios del desarrollo orientado a objetos. Significa que la "teoría" no es dispensable.
Las siguientes secciones discuten algunos de estos principios y examinan, a través de los ejemplos, cómo estos promueven la reusability y reducen el tiempo de desarrollo.

Delegación
La delegación es el proceso donde un objeto pasa la responsabilidad de una acción a otro objeto diferente. Esto normalmente se hace debido a las responsabilidades naturales de los dos objetos en cuestión. Por ejemplo, considere que un botón de comando es responsable de cerrar un formulario. ¿La responsabilidad del botón es reaccionar al usuario cuando este pulse el botón, pero es el botón el que tiene la responsabilidad de cerrar el formulario? Quizás el propio formulario está mejor preparado para realizar su propia acción de cierre. A través de la delegación podemos llevar el paso de la responsabilidad del botón, que es de cerrar el formulario, al propio formulario.
Ejemplo del botón cmdExit
El código del evento click del botón delega la responsabilidad al formulario mediante una llamada al THISFORM.Release() quitando así la responsabilidad del botón para manejar los detalles de cerrar el formulario.
Con el uso apropiado de la delegación separaremos la funcionalidad de una operación en particular y asignaremos la responsabilidad al objeto más apropiado. Esto proporciona la habilidad de ejecutar independientemente pedazos de esa funcionalidad, que a su vez reduce los requerimientos para los procedimientos monolíticos (métodos) que son esencialmente grandes estructuras DO CASE. Estos procedimientos monolíticos son una molestia mayor durante la fase de mantenimiento de un proyecto.

Herencia
Definido como la posibilidad que tiene una clase de incorporar toda o una parte de otra clase en su propia definición, la herencia probablemente es la mayor propiedad del desarrollo orientado a objetos y, al mismo tiempo, el más usada. La herencia es el proceso por el que una subclase recibe conductas y datos de su clase antecesora. La idea es que mientras más extenso es el árbol de herencia la definición de las clases se vuelven más especializadas.
Mientras que la herencia un rasgo poderoso de los lenguajes orientados a objetos, también es limitando. Siempre que intentemos modificar una clase en la jerarquía también necesitaremos determinar lo que este cambio afectará a todas las clases descendientes. Cuando un árbol de herencia crece hasta llegar a ser muy grande la habilidad de reusar cualquiera de las clases individuales dentro de ese árbol será más difícil.
Sin embargo puede usarse la herencia para mantener el polimorfismo. Definiendo propiedades y métodos públicos de una clase entera de objetos en la cima del árbol de herencia podemos estandarizar la interface de programación y asegurarnos que las clases sean intercambiables.

Polimorfismo

Polimorfismo: La capacidad que tiene una funcionalidad de aplicarse en distintas formas. Hay dos tipos de polimorfismo:
Inherente: La funcionalidad es heredada por cada subclase.
Ad hoc: Las distintas funcionalidades de tipos diferentes tienen el mismo nombre.
Definiendo una clase abstracta que introduzca todos los métodos y propiedades públicas de un árbol de la clase entera nosotros podemos mantener consistente la interface de un programador y asegurarnos que todas las subclases sean intercambiables. En otras palabras, si todas las clases de cuadros de texto tienen el mismo conjunto de propiedades y métodos públicos serán intercambiables. Nosotros podemos reemplazar fácilmente un tipo de cuadro de texto por otro sin preocuparnos por algún otro objeto que se refiera a una propiedad o método que el nuevo cuadro de texto no tenga. Éste es el polimorfismo inherente.
Ejemplos del txtBase, txtCalculator, y txtDate
El txtBase define la interface pública para todos los cuadros de texto. El txtCalculator y txtDate heredan de la clase txtBase manteniendo así estándar la interface de programación para esos cuadros de texto y permitiéndoles ser intercambiados sin la necesidad de alterar cualquier código fuera de los cuadros de texto.
El polimorfismo ad hoc puede verse en la clase botón de cmdExit. Sobre Delegación, nosotros mencionamos que el botón pasa la responsabilidad al formulario para la acción de cierre. Esto es cumplido por el código del evento click del botón que llama al método Release del formulario. Los diferentes formularios pueden tomar acciones diferentes en sus métodos Release respectivos y pueden significar que el código en el evento click del botón de salida está aprovechándose del polimorfismo ad hoc para proporcionar reusabilidad. El botón cmdExit puede ser arrojado en cualquier formulario y funcionará eficazmente.


Encapsulación
La encapsulación puede definirse como esconder la aplicación de un objeto. Cuando una clase contiene todos los datos y código requeridos para que pueda funcionar sin la necesidad de depender de cualquier objeto externo al runtime, decimos que está encapsulada. Esta encapsulación permite usar la clase sin tener en cuenta el entorno en donde se la coloca. La clase no requiere nada específico de su entorno y no crea ningún efecto en el lado del entorno. La encapsulación es muy eficaz haciendo clases reusables.

Ejemplo del cntProgress
Se definen todas las propiedades y métodos necesarios como parte de la clase haciendo la clase independiente de cualquier objeto externo al runtime. Esto permite que la clase cntProgress sea virtualmente puesta en cualquier formulario sin tener en cuenta que pueda o no haber en ese formulario.

Independencia
La independencia es un paso para lograr encapsulación. Haciendo una clase independiente requiere que cualquier referencia a las propiedades o métodos de otro objeto sea verificada antes de hacer dicha referencia. Esto es llamado a veces "programación defensiva". Nosotros podemos hacer una clase independiente no asumiendo nada sobre el entorno en el que esa clase puede encontrarse. No asumiendo el significado de verificar que para la existencia de cualquier cosa nosotros necesitamos antes tratar de usarla.
Ejemplo del tmrTimeOutForm
El evento timer verifica la existencia de una propiedad llamada TimedOut en el formulario contenedor antes de intentar hacer una referencia a ella. Esto permite poner el cronómetro en un formulario sin tener en cuenta si ese formulario tiene o no una propiedad llamada TimedOut.

Usando Colecciones
Las distintas clases de base de Visual FoxPro tienen colecciones de sus objetos miembros, entre éstos el Formulario, Container, Grid, PageFrame, Page, etc. Esta colección de propiedades hacen posible referirse al objeto miembro contenido sin saber algo de ellos, como sus nombres o el número de ellos que existen en el objeto. Usar estas colecciones nos hacen posible escribir código que trabajará con una variedad de configuraciones del objeto contenedor.

Ejemplo del grdRefresh
La colección de Columnas es usada en los eventos Init y BeforeRowColChange para permitir que la clase funcione normalmente sin tener en cuenta el número, o orden, de las columnas en tiempo de ejecución.
Además de usar las colecciones que Visual FoxPro nos proporciona nosotros podemos crear también nuestras propias colecciones. Agregando propiedades de array a nuestras clases que mantienen referencias a una colección de objetos pueden permitirnos manejar una variedad de situaciones sin "codificar duro" las referencias de objetos.


Referencia Indirecta
El uso de la referencia indirecta a los objetos quita la dependencia en los nombres de esos objetos. Dependiendo de nombres de algo en una definición de una clase limita la reusabilidad de esa clase. En lugar de codificar los nombres de cosas en una clase es mejor usar las palabras claves THIS, THISFORM, y THISFORMSET. Una de las preguntas más comúnes con la que yo tuve que tratar durante una clase de capacitación es: "¿Cómo consigo el nombre de mi formulario para que yo pueda referirme a él?". Más allá del interrogatorio normal descubrimos que el estudiante quiere cambiar una propiedad del formulario desde uno de los controles contenidos dicho formulario. Usando THISFORM hace que esta pregunta sea discutible, no se necesita el nombre del formulario. De hecho el formulario puede tener su nombre cambiado y el código todavía trabajará correctamente.
Otro problema que puede surgir es que un objeto necesite comunicarse con su contenedor. Yo he visto a mucha gente escribir código en un textbox de una columna en un grid de esta manera:
THISFORM.Grid1.Refresh()
El problema con el anterior segmento de código es que fallará si usted coloca el grid en una página de un PageFrame. Falla porque la ubicación del grid ha cambiado de THISFORM.Grid1 a THISFORM.PageFrame1.Page1.Grid1. Las código anterior hace que sea imposible de reusar el grid a menos que se coloque en exactamente el mismo lugar. Reescribiendo el anterior código usando la propiedad PARENT del objeto hace que el código sea movible.
THIS.Parent.Parent.Refresh()
Este código todavía se refiere al grid sin embargo se dirige al grid desde el textbox en lugar del formulario permitiendo poner al grid en cualquier parte sin afectar el funcionamiento del código escrito en el textbox.


Composición
La composición es la construcción de una clase compleja mediante la combinación clases más simples en una clase contenedora. A través de la composición usted puede crear una sola clase que tenga muchos controles en ella. Arrojando esta sola clase en un formulario le dará una interface relativamente compleja sin la necesidad de construir cada parte de esa interface para cada formulario que lo usa.

Ejemplo del cntProgress
La clase barra de progreso es una clase compleja compuesta por varias clases simples puestas en una clase contenedora.
NOTA: la composición, mientras puede mejorar la reusabilidad en ciertas situaciones, también puede presentar problemas. La composición tardía es mucho mejor que la composición temprana. Las designaciones tardías y tempranas para la composición se refieren al lugar del árbol de la clase contenedora donde es poblada con sus objetos miembros. Si esta población toma lugar justo antes que la clase sea usada para instanciar un objeto se llama composición tardía, si esto toma lugar antes de que una subclase del contenedor sea creada se llama composición temprana. La composición temprana causa problemas con la flexibilidad de la clase compuesta haciendo que los cambios al objeto miembro sean más difícil de lograr.

Mediación
La mediación permite que un objeto pueda manejar las comunicaciones para otros objetos. Se ve a menudo en contenedores que mantienen objetos externos con una interface para acceder a las conductas de los objetos contenidos.

Ejemplo del cntProgress
Todas las comunicaciones de los objetos externos han terminado en métodos del contenedor cntProgress. Para aumentar el ancho del shape dentro del contenedor son llamados los métodos del contenedor.

Usando un Framework Estándar
Haciendo estándar un framework para todo el desarrollo de la aplicación pueden documentarse y cumplirse los contratos entre las distintas clases. Todo los sistemas orientados a objetos derivan su conducta a través de objetos que se comunican con otros objetos. Sin la comunicación de objetos no hay ninguna actividad en el sistema.
Como con la comunicación humana, los objetos deben estar de acuerdo en ciertas convenciones. Si dos personas intentan comunicarse y no están de acuerdo primero con en el idioma que van a usar, existe la posibilidad que no va ocurrir ningún intercambio de ideas porque las dos personas no se pueden entender. Lo mismo pasa con los objetos. Ellos deben estar de acuerdo en el "idioma" o sintaxis que usarán sus comunicaciones. Este acuerdo está llamado un contrato entre los objetos. Establecer los contratos para las comunicaciones de objetos es una de tantas cosas que un framework hace por nosotros. Haciendo estándar un framework a ser usado para todo el trabajo de desarrollo de la aplicación nos aseguramos que los contratos de comunicación entre los objetos serán consistentes.
Siempre que empecemos a crear una definición de una clase reusable nosotros debemos decidir si esta clase requerirá los contratos del framework o funcionará libremente sin tener en cuenta el framework. Si nosotros decidimos que la clase debe funcionar fuera del framework, entonces tenemos que tomar medidas para asegurarnos que la clase no tenga ninguna dependencia en los contratos del framework.

Resumen
Los principios del desarrollo orientado a objetos, como Herencia, Polimorfismo, Encapsulación, Mediación, y Composición, sostienen la creación de clases reusables. Aplicando estos principios eficazmente las clases que vamos a crear pueden ser usadas muchas veces sobre una variedad de settings diferentes. Sin embargo, este reusabilidad no viene gratis, nosotros debemos crear activamente nuestras clases para que sean reusables prestando mucha atención a nuestro diseño y codificado.
Hay grados de reusabilidad, como reusable dentro de los límites de un framework particular contra reusable sobre los framework múltiples. El grado más alto de reusabilidad, es el que nosotros debemos planear para la clase que vamos a crear.
Envíe un email a Jim Booth con preguntas o comentarios sobre esta información.