Inicio
He creado este espacio para compartirlo con familiares y amigos, aunque no descarto la posibilidad de que otros visitantes se encuntren a gusto y lo puedan disfrutar tambien...

InicioMapa del sitioDescargasColaboradoresEnlacesAutor    
Buscar :

Ideas


Y encima del sofá... un televisor

¿Realmente merece el televisor ese papel protagónico que solemos darle?


¿Un flat panel sobre mi buró?

¿Y por qué no... "debajo" del buró?


Programe con orden, no con "ingenio"

El diseño de toda aplicación de software comprende dos aspectos: (1) La modelación del problema a resolver; (2) Las técnicas de implementación de ese modelo. En los textos "clásicos" de programación se hace mucho énfasis en el primer aspecto; yo me propongo en este artículo poner el énfasis en el segundo, por considerarlo tan (si no más) importante que el primero.




Armando Acosta  (10-25-2007)

Hay dos maneras de programar: (1) Tomándose el tiempo de planificar mucho antes de escribir la primera línea de código; (2) Comenzar a escribir tan pronto como se tenga esbozada, apenas, la idea general del proyecto.

Todo programador de experiencia sabe que la primera es la vía que mejores resultados da; pero también sabe (por experiencia) que diseñar en frío es la mar aburrido, y además (y sobre todo), que por mucho que se planifique, habrá siempre que modificar el diseño original en algún momento: no se pueden prevenir en la fase de diseño todos los problemas que aparecerán en la práctica de la codificación.

¿Qué hacer entonces? En mi experiencia, partir de una buena "base".

Siempre se parte de un "esqueleto": las primeras clases, los primeros módulos. Poco a poco se irán escribiendo otros módulos que harán uso de los primeros; es como construir un edificio. Pero si la base no es sólida, la estructura terminará desplomándose, especialmente cuando la obra se encuentre muy avanzada ya.

Pongamos un ejemplo.

Digamos que me han pedido escribir una clase para manejar comandos entrados por el teclado. ¿Por qué querrá este cliente una interfase de comandos en lugar de una gráfica? Bueno, ese es su problema... el mío es darle solución.

Comandos... Si me remito a los comandos de DOS (para irme haciendo idea) veo que un comando puede ser simple, como "DIR", o puede tener argumentos, como "XCOPY c:\Source Q:\ Backup\Source\ /A /D /E /C /I /R /K /Y > BK_Source.log". Se me ocurre que la solución es tan simple como manipular el string (un comando no es más que eso, un string) para identificar el nombre del comando y sus argumentos, y una vez obtenidas estas piezas, escribir acciones para ellas.

Esto es, por supuesto, una "mala base". No llegaré muy lejos antes de que los "IF~THEN~ELSE" se vuelvan impenetrablemente macarrónicos. Es mejor que analicemos el asunto con más detalle.

Un comando tiene, en efecto, un nombre y (opcionalmente) un conjunto de argumentos. Pero también tiene un contexto donde ese comando es válido, y una acción asociada al comando que tal vez puede variar en dependencia de los argumentos y/o del contexto. Pudiera también tener una breve descripción para mostrar en un help; pudiéramos pensar incluso en un comando "help" que indique al usuario cuales comandos están disponibles dentro del contexto actual. En fin, un comando en un elemento tan complejo que merece tener su propia clase ¿O no?

Así lo creo. Mi primera dedición entonces es escribir una clase llamada "C_Command" que va a caracterizar completamente a un comando, cualquiera que este sea.

Contexto... validez del comando dentro de un contexto dado... ¿Cómo voy a manejar esto? Veamos.

Yo sé que siempre que el usuario esté pronto a entrar un comando, habrá un "contexto", y un conjunto de comando válidos en ese momento (dentro de ese contexto). Mi segunda tarea es encontrar un lugar para ese conjunto (variable) de comandos. ¿Un arreglo? ¡Por qué no!... y he aquí que mi entusiasmo ha estado a punto de llevarme a la macarronicidad. Implementar mi conjunto de comandos en un simple arreglo sería un error, una mala base.

En efecto, los comandos no siempre están aislados los unos de los otros; pueden existir "secuencias de comandos". Pensemos, por ejemplo, en un comando "assign key", cuya ejecución lleva al usuario a una lista de keys de la cual tiene que elegir una, luego de lo cual (y solo entonces) se ejecutará la asignación. Y digamos también que, una vez ante la lista de keys, el usuario se puede arrepentir y presionar la tecla ESC para volver atrás.

Se impone entonces encontrar un lugar donde representar una "secuencia" de comandos que pueda recorrerse hacia atrás y hacia delante. Un simple arreglo pudiera servir, pero terminaríamos con una intrincada manipulación de apuntadores al arreglo. Parece que lo que se impone aquí es una pila (stack) de objetos de tipo C_Command.

Y aquí tengo mi segunda dedición: Implementaré una clase llamada C_Commands_Stack". Dentro de esta clase habrá un arreglo de objetos C_Command, por supuesto, pero también estará, encapsulada dentro de la clase, toda la manipulación de apuntadores al arreglo, creación y destrucción de objetos, etc. La clase representa un stack y se comporta como tal; tiene dos métodos: Push y Pop, y una propiedad llamada "CurrentCommand" que retorna el comando que está en el tope del stack (es decir, un objeto de tipo C_Command).

El código cliente puede crear más de un objeto tipo C_Commands_Stack. Por ejemplo, un stack principal, y stacks auxiliares para manejar las secuencia de comandos a medida que estas se vayan gestando en tiempo de ejecución.

Parece que vamos bien. Tenemos un lugar para los comandos y otro para los conjuntos de comandos. Pero ¿qué hay de la identificación de los argumentos? De nuevo nos enfrentamos al peligro de sentar bases endebles: si me conformo con usar instrucciones de manipulación de strings para extraer los argumentos del comando, me arriesgo a generar código intrincado, difícil de mantener en el futuro. Veamos cómo hacerlo de manera más contundente.

Asumamos que desde diseño (y en conformidad con mi cliente), he llegado a la siguiente definición del comando:

       cmd_name [arg1 arg2... ]

Es decir, un nombre del comando opcionalmente seguido de argumentos, todos separados por espacios.

A su vez, cada argumento consta de dos partes:

       arg_name=arg_value

O sea, el nombre del argumento, signo igual, valor del argumento, todo sin espacios.

Ejemplos:

       help

       list keys

       list key=test

       list key=test title=lechuga

Por supuesto que puedo utilizar instrucciones de manipulación de strings en el código cliente, pero ya habímos renunciado a ello ¿No?. Mi solución será esta otra: escribiré una clase llamada C_Argument con dos propiedades: Name y Value. Dentro de la clase C_Command (¡Y gracias a Dios que implementé el comando como una clase!) coloco un arreglo de objetos C_Argument. Cada vez que el código cliente identifica un comando, se crea un objeto C_Command y dentro de él ya existe código para extraer automáticamente los argumentos y depositarlos en su arreglo interno de C_Argument. Desde "afuera" (el código cliente) los argumentos se ven como una colección accesible desde dos propiedades que también he implementado en C_Command: ArgumentsList(Index) y ArgumentsListCount.

No quiero aburrir con más detalles de un ejemplo tan concreto, así que voy a detenerme aquí para analizar las ventajas de mi implementación. Recuérdese que el punto es crear una base (un esqueleto) y esa base será buena en la medida en que se pueda expandir sin que se enrede demasiado ¿Lo habremos logrado? Veamos.

Digamos que en este punto a mi cliente se le ocurre que los comandos deberían tener un alias. Como he creado un objeto para representar el comando, tengo un lugar concreto donde escribir todo aquello que lo caracteriza, de modo que el único lugar para el alias es la clase C_Command. Será tan fácil como añadirle una propiedad llamada Alias. Todo código que necesite hacer uso del alias (mi presunto comando "Help", por ejemplo) solo tendrá que leer (o escribir) esta nueva propiedad.

¿Demasiado sencillo? Veamos otro ejemplo.

Mi cliente traiciona la definición de comando que habíamos acordado. Ahora necesita un comando concreto, "find arroz con pollo", que no cabe dentro de esa definición. Me explica que el nuevo comando "find" tiene un solo argumento, que es un keyword ("arroz con pollo" en el ejemplo), y este puede contener espacios. ¡Fatal! ¡El código que yo había escrito entiende los espacios como separadores de los argumentos! ¿Qué hacer? Veamos qué se me ocurre.

Me doy cuenta de que la nueva definición de comando es, en realidad, igual que la anterior, solo que ahora ciertos argumentos pueden contener espacios. ¡Ya lo tengo! ¡No hay nada que modificar, solo añadir!

En la clase C_Command implemento una nueva propiedad llamada RawArgument; en ella se deposita todo lo que no sea el nombre del comando. El código cliente (y no el mio) es quien conoce las particularidades de cada comando; sabe si el argumento de un comando concreto es de tipo arg_name=arg_value o un string con espacios (como en "find arroz con pollo"). En el primer caso, leerá mi propiedad ArgumentsList(Index); en el segundo, leerá mi nueva propiedad RawArgument.

El hecho de haber solucionado estos problemas de diseño mediante la adición, y no la modificación, de código, es una buena señal. De hecho es lo que siempre queremos hacer, ir edificando la solución, no reescribiéndola a cada tropiezo. Cada vez que reescribamos (cosa de la que, dicho sea de paso, nunca escaparemos del todo), estaremos revelando la inefectividad de nuestro código.

El punto es, en definitiva, que cuando nos sentemos a programar, no importa cuan tarde o temprano dentro del ciclo de desarrollo, nunca debemos conformarnos con implementaciones endebles prestas a tornarse intrincadas a medida que el proyecto avanza.

Cada nueva estructura en nuestro programa debe tener un potencial de desarrollo, que es lo mismo que decir, un "poder de encapsulamiento".

Programar es un arte, y como en todo arte ha de primar la belleza. Y la belleza de un código radica, no tanto en el ingenio como en la organización. Recuérdese siempre la regla de oro de la organización: Un lugar para cada cosa... y cada cosa en su lugar.


  • Otros articulos técnicos


  • Imprimir   Enviar a un amigo   
                                                    

    Miami / USAmail@armandoacosta.comInicio