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:
- Saber que la regla existe.
- Saber lo que es la regla.
- Saber por qué las reglas existen
- 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.
No hay comentarios. :
Publicar un comentario
Los comentarios son moderados, por lo que pueden demorar varias horas para su publicación.