Codificar para el cambio

By: Mario Alejandro Montoya C.

Abstract: Simples pero útiles técnicas para afrontar (y sobrevivir!) a los cambios constantes de tecnologias

     Codificar para el cambio

Desarrollo rápido de aplicaciones... drag & drop...haga clic aquí y allá, conecte esto con aquello, compile y despliegue en 10 minutos! ¿Familiar? Hmmm...Me recuerda a esto:

Hide image
Click to see full-sized image

Imagen 1: Típico formulario de acceso a datos

Pero...un momento!!!! Si esto es lo que hace "bello" a Delphi!... ¿Seguro?.

Código espagueti...lógica de datos, de negocios, validaciones en los eventos, en todas partes... decenas de controles de acceso a datos, múltiples formularios e informes.

Entonces, que se yo...por alguna razón a la gente le da por cambiar de motor de bases de datos...Increíblemente, a los que hacen estos motores les da por cambiar las tecnologías de acceso a esos datos (de DAO, ADO, ODBC, JDBC, ADO.NET, BDE, IBX...).

Luego, vienen los cambios de plataformas (como de DOS a Windows, luego a .NET), los cambios entre versiones de la herramienta...los cambios entre versiones de las librerías de...continuo?


Y aunque quienes programamos con Delphi sabemos que a diferencia de la distingida competencia tenemos una plataforma mucho más estable, aún asi, los cambios llegan.

Cambios. ¿No le gustan? Mala suerte. Si lleva un tiempo en este negocio ya ha tenido bastantes. Tal vez esta en el proceso de migración de una aplicación de Delphi Win32 a Delphi.NET (bueno, a algunos se le ocurre) y … ¿muchos formularios, informes? Aahhh, ¿con que ahora le pico la mosca de los servicios Web y aplicaciones de Internet? Pues sabe, te darás cuenta que el asunto no va a salir fácilmente (si, Delphi hace fácil la transición, pero como veremos el problema esta ubicado entre el monitor y el teclado). Así que vamos a visualizar una manera intuitiva de ver como se codifica para el cambio (y de paso hacer un poco las paces con nuestra herramienta).

Podría tratar de explicar términos más impresionantes como multi-nivel, MVC, patrones. No es mi intención dar explicaciones formales (porque si usted es como yo, todavía no ha podido salir de la programación orientada a objetos, del UML poquito, poquito y además multi-nivel…como si no fuera suficiente lidiar con los clientes!) Y ya que mencione una forma intuitiva de ver las cosas, para variar veremos que los elementos de un desarrollo, el código y los componentes están en unas de estas categorías:

• Las cosas que van a cambiar seguramente

• Las cosas que cambian los demás

• Las cosas que debemos evitar que cambien

• Las cosas que cambiamos nosotros – no importa que tan seguido - o nuestros usuarios – que es por lo que nos pagan 

Nos va a interesar defendernos de la primera y la segunda, pero procuraremos mucho poder hacer la tercera y la cuarta. Las cosas que cambian seguramente y que dependen de los demás (principalmente, productores de plataformas como sistemas operativos, bases de datos, Web; y controles esenciales, como reporteadores y grids) son los más complicados y los que causan más estrés en la medida que un desarrollo va creciendo, y surge la necesidad del cambio.

     Las cosas, que van a cambiar seguramente, y que cambian los demás.

Abra Delphi…échele una mirada a la paleta de componentes. ¿Cuántos tabs de acceso a datos? En Delphi 7 Enterprise, unos 7. Una señal inequívoca que el acceso a datos cambia MUCHO (algo similar con las de tecnologías de Internet). De hecho, existen muchos más, que optimizan el acceso a esto o aquello…. Los componentes de acceso a datos son candidatos perfectos para causar problemas de migración. Para entender adonde voy, échele una mirada a este código:

program Project1;

{$APPTYPE CONSOLE}
uses SysUtils;
var Val1:Integer; Val2:Integer; lcOut:String;
begin Val1:=1; Val2:= 2; lcOut:='Total: '+IntToStr(Val1+Val2);
Writeln(lcOut); Val1:=3;
Val2:= 4;
lcOut:='Total: '+IntToStr(Val1+Val2);Writeln(lcOut);
Readln;
end.

Un código de novatos… pero con el tiempo el novato hace esto, instintivamente:

program Project1;

{$APPTYPE CONSOLE} uses   SysUtils; function Sum(Val1:Integer;Val2:Integer):String ;
var
  lcOut:String;
begin
lcOut:='Total: '+IntToStr(Val1+Val2);

  result:=lcOut;
end;

begin   Writeln(Sum(1,2));   Writeln(Sum(3,4));   ReadLn; end.

¿Y la razón? ¿Menos líneas de código? Eso, y el hecho que hacer un cambio al código es más simple alterando la función Sum que cambiando las líneas del programa 1 (por ejemplo, sumar no solo integrales, sino números reales).

     Más fácil cambiar mientras menos lugares haya que cambiar

Ahora, un ejemplo más practico:

Hide image
Click to see full-sized image

Imagen 2: Típica aplicación que me dará dolores de cabeza cambiar. Me preocupa saber que código hay detrás y que rayos hará...

Pues aunque suene increíble, este tipo de “código” es muy común.. El que no lo hagamos en el editor de texto no lo hace menos código. Un programador aún sin entrenamiento formal tiende a colocar el código escrito que se repiten en funciones o clases.

Pero cuando se trata de componentes que se ven en diseño parece que no ve que puede HACER LO MISMO.

Los controles de acceso a datos cumplen con 1) Cosas que no se ven 2) Cosas que los demás cambian 3) Cosas que SEGURAMENTE cambiaran. Mala idea poner MUCHO de eso en distintos lugares porque: “Más fácil cambiar mientras menos lugares haya que cambiar”. De ahí que muchos usan los TDataModules, porque es una buena idea.

Por otro lado, el TDataSource es distinto. Aunque parece cumplir con los mismo defectos que los TDataSet y sus descendientes, resulta que 1) Realmente no hace NADA … solo mapean datos a controles 2) Interfaza a los TDataSet 3) Cambiar de un TDataSet a otro no altera el FUNCIONAMIENTO de los TDataSource. Y resulta que las cosas que cumplen con estos 3 elementos son buenos SIMPLIFICANDO EL CAMBIO.

     Las clases abstractas y el uso de interfaces simplifica el cambio.

Tener muchos TDataSource realmente no es problemático y al contrario es útil para posicionar los elementos de los formularios, los informes y declarativamente, asignar el enlace a datos. TDataSource es un buen chico: No causará líos cuando lo demás cambie.

(PS: TDataSet es una clase muy bella: Presenta de forma unificada una colección de filas y columnas y la manera de alterarlos. Sin embargo, el enfoque esta en como la proliferación de muchos componentes que DEPENDEN de otros que tienen implementaciones DISTINTAS que cambia en el tiempo se debe evitar. TDataSet esta muy bien, que nos cambien todo el tiempo de tecnologías de acceso a datos es el punto a notar).

El problema radica en que, en todo tutorial y ayuda, siempre vemos los TDataSet y TDataSource juntos.

     Los tutoriales y las ayudas explican funcionalidad, no arquitectura

Pero eso no tiene que ser así. Si los cambios se simplifican disminuyendo la cantidad de los componentes/código que SEGURAMENTE va a cambiar, que tal si lo hacemos así:

unit Unit2;

uses SqlExpr, DB;

interface type   TDataManager = class(TObject)
  private   protected   
function OpenConnection(): TSqlConnection;
 public 
   
   function ExecuteAsDataSet(lcSql : String): TSQLDataSet ;
function ExecuteNoDataSet(lcSql : String): Integer ;
 end;

implementation end.

Pues ahora tenemos UNA SOLA CLASE que implementa el acceso a datos. Es una facilitadora del cambio: un solo lugar donde chequear. Una interface que esconde lo que realmente no se necesita (por ejemplo, la conexión, la necesita ExecuteAsDataSet, pero no se necesita por fuera de la clase... no, realmente no se necesita).

Fácil de cambiar el comportamiento (OpenConnection puede crear la conexión cada vez que se llama, obtenerla de un componente dentro de un TDataModule o usar un sistema de pooling para mejorar rendimiento en ambientes exigentes, sin alterar el funcionamiento externo).

Luego un formulario base que enlace los TDataSource, de ahí, una función que cargue la clase de acceso a datos, y conectar el TDataSet(cualquiera) a los TDataSource. Si fuésemos a cambiar de dbExpress a ADO, seria cambiar los tipos de datos de vuelta y el código interno de crear y conectar controles…implica igual cambio, pero es MUCHO mejor que alterar múltiples formularios (porque la idea es que quitamos todos los TDataSet de encima de los formularios). Es un gran avance hacia un código más administrable y flexible… Y luego, hasta podríamos pensar en soportar varias tecnologías de acceso a datos… luego, aún más elegante, no llamar el acceso a datos en los formularios, sino solo tratar con clases abstractas, de interface con la funcionalidad del negocio - como hace Bold / ECO, presentes en las versiones Architect de Delphi -(recuerde: este tipo de clases son buenos chicos: facilitan el cambio); y es más fácil pensar en estas metas porque es menos descabellado alterar UNA clase que muchos formularios, además, si cambiamos los tipos de datos el compilador de Delphi nos dirá donde hay que hacer los cambios...

¿Y por qué detenerse ahí? Y que tal si en vez de tener cadenas de SQL diseminadas en MUCHOS lugares los ponemos en un solo lugar (una lista de constantes, una base de datos local, un archivo de texto o XML)…¿o crear una clase que construya el SQL en base a una lista de criterios?

El punto es que entender el cambio ayuda a construir mejor código, que tiende a abrir las posibilidades y permite reaccionar más fácilmente a los constantes ires y venires del mundo tecnológico.

     Las cosas que debemos evitar que cambien, y nos permiten cambiar

Estas cosas es lo que se llama comúnmente la lógica del negocio…o sea si es un programa de facturación, son los códigos y componentes que hacen SOLAMENTE facturación, no pintar cuadros, no imprime informes, no manda correos. Un programa de análisis numérico, es el código que hace el análisis.

Para evitar que las cosas que SEGURAMENTE van a cambiar cambien lo que debemos EVITAR que cambien (=no debería alterar la lógica de la aplicación), pues debemos dividir las responsabilidades. Ejemplo:

function TForm1.CalcularSalario: Double;
begin   result:=EditBaseSalario.Field.AsFloat*30;
end;

Es un código que no debemos permitir que se cambie. O sea, la aplicación DEBE calcular el salario. ¿Cómo podría arruinarse el código? Bueno, tal vez el campo de la base de datos cambia, tal vez cambiamos de controles enlazados a datos (nos gusto una librería con su propia versión de un Edit...). Pusimos un MessageBox para informar al usuario pero luego queremos pasar la aplicación al Web, una aplicación externa debe conectarse a la nuestra en tiempo real a través de COM o de un Servicio Web. Ahora, no solo el código hay que cambiarlo, será COSTOSO hacerlo. Y si existe mucho código de este tipo existirá la tentación de hacer “truquitos” y no hacer las cosas bien.

Sin embargo algo como:

function TEmpleado.CalcularSalario( BaseSalario : Double ): Double;
begin   result:=BaseSalario*30; end;

Ya es mejor. Y como estoy inspirado, una mejora más!

function TEmpleado.CalcularSalario( BaseSalario : Double ): Double;
begin   result := BaseSalario * DiasEnMesContable;//Eliminado el número mágico. Ahora compila los martes 13 ;) end;

Conectar clases es fácil, y más cuando las clases son tan independientes, que no saben si se conectaron con un formulario o un servicio Web.

Si colocamos el “nucleo” de nuestra aplicación en clases bien definidas que hacen solo su trabajo (y el de nadie más), y usamos parámetros que no sean controles ni clases que no tengan que ver con la lógica de la misma (en la medida de lo posible, mejor pasar estructuras o clases informativas o simples variables), lograremos un sistema más inmune a los cambios de arquitectura, de base de datos o de controles visuales, e incluso a los propios.

Y no tuvimos que reescribir la aplicación desde CERO!  Woow! Santos ahorros de tiempo y dinero!

Esto que acabe de hacer se llama refactorización que en pocas palabras es mejorar el código fuente sin alterar su comportamiento (= no introduce nuevas características y claro, no introduce más errores!). De paso, asegure que el código del cálculo del salario sea reusable, y podemos luego llamarlo desde un servicio Web, una página de HTML, una página WAP para celulares, etc...

Pueden aprender más sobre este tema leyendo este artículo (inglés) de Jim Cooper. Aunque la técnica de refactorizar se puede hacer de forma manual, como acabo de hacer, con Delphi 2005 es mucho más fácil!

     Separar responsabilidades (quien muestra, quien procesa, quien comunica) siempre es una buena idea.

Y no solo es fácil de cambiar, es fácil de portar. Portar a Linux o a .NET, código de este estilo, es fácil. Integrar es fácil, solo necesitamos colocar tecnologías puentes como SOAP. Ok, cambiar de componentes visuales puede dar algo de dificultad y es muy probable que se gaste unas buenas semanas, incluyendo el esfuerzo en volver a probar el código.

Tal vez, exista un componente que hace algo importante, como presentar informes… Pero es algo que se puede solucionar cambiando de componente. No una solución ideal, pero pragmática, al fin y al cabo, nuestro trabajo no era hacer el reporteador, es hacer facturación.

Una posible excepción, podríamos pensar, son los mismos controles visuales. Por ejemplo, ¿que tal si nuestro trabajo no es un sistema de facturación sino crear controles, como un control de menú, que estaba en Win32 y ahora debe estar en Delphi.NET con Winforms o ASP.NET? ¿No porta, verdad?

Bueno, una forma de separar responsabilidades es tratar de ver el problema de la manera más simple, de entender que realmente es lo que se debe de hacer, no como.

¿Que hace un menú? Es algo como una estructura de árbol. Debe hacer clic, debe mostrar iconos…bueno todos sabemos que hace un menú.

Lo importante: Debe hacer lo mismo no importa la plataforma, ni el entorno. Un menú es buen menú, si hace lo que un menú tiene que hacer.

Estos meses estuve probando entre unos 5 diferentes menús de distintos fabricantes para ASP.NET de un sistema ERP que estoy haciendo. Al final nos quedamos con una librería, básicamente, porque nos salía económico el paquete. Lo más molesto es que me tocaba recodificar los menús cada vez que probaba uno...mucho código. Eso me harto, e hice mi propia representación virtual de un menú: Arme una colección con la misma estructura de menú, con las propiedades que todo menú tiene y así por el estilo. Luego, para cada API de estas librerías, UN SOLO CICLO armaba en tiempo de ejecución el menú, ajustado a la forma particular de cómo cada sistema quería hacerlo (unos sistemas requerían armar un archivo XML, otros tenían un API OO, otros de forma mixta). Todo el trabajo no gasto más de 4 horas y ahora, ya que el menú es virtual, lo puedo pasar de plataforma y de controles sin gastar más de 1 día de “Duro” trabajo (cambiando los colores o ajustando los efectos visuales, principalmente), y por ahí también aproveche la clase para fabricar un navegador estilo árbol para el sitio, de ahí a un mapa del sitio es solo un pequeño paso. De hecho, usar representaciones virtuales de los controles gráficos es lo que terminan haciendo los que fabrican componentes, como los grids, reporteadores y demás.

Por eso es que esas empresas logran tener productos a otras plataformas y herramientas tan rapido. Usted tambien puede. El resto de trabajo es pura carpinteria (que no es que la carpinteria sea fácil... es solo un poco mas predecible)

En conclusión, si protegemos el código de la lógica de los negocios (que es la CLAVE de nuestro programa: Todo lo demás es añadidura) estamos protegiendo nuestra libertar de cambiar … evitando que lo demás nos cambie.

     Las cosas que debemos aprender

Hay muchos lugares donde podemos aprender de buena arquitectura, aunque es cierto: es difícil de asimilar y más difícil de aplicar si ya somos lobos viejos y con mañas. Pero aunque la aplicación no tenga los últimos adelantos en arquitectura y diseño, aplique todos los patrones de este mundo y emplee las mejores prácticas posibles de desarrollo, no hay excusa para torturarnos a la hora de cambiar el código. Aquí, algunas cosas que pueden ayudar:

     FAqs para cambiar código felizmente

Nota: los links son a páginas en inglés...

¿Cómo cambiar código y tener seguridad de no quebrantar la integridad de la aplicación?

Algunos dicen que los conceptos de los procesos Agiles y la programación extrema son útiles. En especial, el de probar continuamente y con la ayuda de herramientas de prueba. Muchos usamos con Delphi, DUnit para lograrlo (DUnit ya esta integrado en la versión de Delphi 2005!). El hecho de tener un “robot” de pruebas que haga el trabajo sucio de probar repetidamente, facilita la reestructuración del código y mejora la arquitectura interna.

He leído el artículo, y aún no me queda muy claro como aplicar las ideas en otras áreas…

Bienvenido al club. He leído bastante sobre el tema y da dolor de cabeza tratar de ver como realmente aplica, a diferencia de un algoritmo, la arquitectura se basa más en ideas que en código concreto. Una manera de ver como aplicarlo a situaciones concretas es con la ayuda de la comunidad. Los newsgroups de Borland tienen grupos especializados en arquitectura y diseño, puedes visitar el de Delphi dando clic aquí. También se aprende con base en el ejemplo.

Algunas librerías que exponen muy bien estos principios son ECO/Bold (vienen con Delphi), casi todo lo que viene con SOAP (también viene con Delphi) y servicios Web, de hecho Delphi tiene diseminado en todas partes a mayor o menor grado estos principios. Hay librerías de terceros que exponen un buen diseño, como las de RemObjects, MsgConnect, DevExpress, TechInsite y otros. No, no le estoy vendiendo estos productos, pero vale la pena por lo menos analizar el concepto (=lea los artículos de los conceptos de diseño). Así como un artista se inspira viendo el trabajo de otros, nosotros nos podemos inspirar en las ideas de diseño de otros desarrolladores.

Donde aprender más

Puede ver código de bloques prediseñados visitando la página de MS, el que más me gusta y muy útil para Delphi.NET, además de no ser muy difícil de cambiar a los BDP si se desea es el Data Acces Application Block (recuerde: Puede usar Babel Code para pasar el código de C# a Delphi!). No le tenga miedo a visitar a las páginas de otros fabricantes y preocúpese aún menos de que usen lenguajes y herramientas diferentes, igual, ya sabemos que tenemos lo mejor de lo mejor pero ¿porque no aprovecharnos de la experiencia de los demás?

Aprender sobre patrones de diseño ayuda bastante. Entender algo de UML también. De hecho, entender la arquitectura de cualquier cosa, como las bases de datos, la Web y demás ayuda bastante. Es como la arquitectura tradicional, hasta saber como se hace un puente ayuda a ver como hacer una casa. Ver como otros resuelven sus problemas, entendiendo que la arquitectura no es código sino concepto, es clave.

¿Qué más puedo hacer?

Puede empezar por mirar cuanto código duplicado (tanto en forma de texto escrito, como de forma visual en controles y formas) tiene su aplicación y empezar a agrupar y concentrar en pocos lugares. No olvide que los recursos (como cadenas de texto, constantes, variables, imágenes) también son más fáciles de administrar si los ubica en lugares concretos y automatiza el acceso a ellos. Asegúrese de mantener el proceso controlado, vuelva a probar (manualmente o con DUnit) que el código se comporta correctamente, y no se deje tentar por hacer solo una "mejoría" más al sistema. Para mayor eficacia, mire en donde tiene concentrado la mayor cantidad de código y empiece por allí.

Estoy listo, voy a tirar a la basura mi aplicación y voy a empezar de nuevo!

Si eso es lo que quiere.... pero tal vez es mejor ir haciendo cambios graduales. Si su aplicación es de un largo ciclo de vida (como lo son Office, Windows, ERP, Software vertical) se obtiene un mejor balance si se hacen cambios, poco a poco, que empezando de nuevo. No olvide: es importante liberar la aplicación algún día. 

Mario Alejandro Montoya C.

Gerente, MCP

Hide image
Logo de Soluciones Vulcano

http://www.solucionesvulcano.com

Server Response from: ETNASC04