[All]
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:
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:
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
http://www.solucionesvulcano.com
|
|
Connect with Us