25 de septiembre de 2009

Ámbitos y Alcance en Javascript

Dependiendo de los lenguajes programación en los que hemos programado con anterioridad, en Javascript nos podemos topar con algunas sorpresas, y no todas ellas son agradables, Específicamente, puede ser un problema si venimos de un background de lenguajes con alcance de bloque (c, c++, java, etc.) o sin él (basic, pascal, etc.). Ya que el alcance en Javascript puede ser la fuente de bugs muy difíciles de encontrar, a continuación hacemos un pequeño recuento de cómo funcionan los ámbitos de variable en Javascript.

El ámbito "global"

En la mayor parte de los navegadores web actuales, el objeto window es el espacio global que contiene todas las funciones base del lenguaje y todas las variables y funciones que serán definidas en este.

Si se declara una variable, usted verá que usted puede hayarla en el objeto window:

var miAncho = 100;
miAltura = 200;

alert( window['miAncho'] ); // "100"

alert( window.miAncho );    // también muestra "100"

alert( window['miAltura'] ); // "200"
alert( window.miAltura === miAltura ); // "true"

Obviamente, no todo es accesible desde el objeto window, ya que en Javascript podemos usar el ámbito de nivel de función.

Declaración de variables

Tal vez esto sea obvio para muchos, pero para mi inicialmente fue una duda que durante algún tiempo limitó mi entendimiento de Javascript. En Javascript, es perfectamente válido declarar una variable con la palabra reservada var ¡O sin ella! (a todos los que hemos programado en Basic alguna vez, nos trae hermosos recuerdos de lo grandioso que nos parecía esta "característica"... hasta que nos ocasionó nuestro primer dolor de cabeza).

La idea básica que debemos entender es que var no es solo lo que la abreviación parece indicar: una declaración de una variable, sino por el contrario, es la definición del alcance de la misma. En el ejemplo anterior hemos usado ambas notaciones y podemos ver que en el caso del alcance global, el ámbito de la variabe no se modifica.

// Al estar en el ámbito global, ambas lineas
// son equivalentes
var miAltura = 100;
miAltura = 100;

En el especio de nombres global, la instrucción var no tiene ninguna repercusión, por lo que podemos hacer algo como esto:

var miAltura = 100;
miAncho = 100;

function muestraVariables() {
 alert( miAltura + ' y ' + miAncho);
}

Las variables declaradas en el ámbito global serán accesibles desde dentro de todas las funciones, pues todas están también contenidas en el objeto window. Sin embargo, las cosas cambian cuando usamos var en el contexto de funciones. A continuación vemos un ejemplo en el que var modifica el alcance de una variable:

var miAltura = 100;
miAncho = 100;

function muestraVariables() {
 alert( miAltura + ' y ' + miAncho);
 var miColor='#000000';
 miFondo='#FFFF00';
}

function muestraColores() {
 alert( miColor + ' y ' + miFondo );
}

muestraVariables(); // "100 y 100"
muestraColores();   // "undefined y #FFFF00"

Aquí es conveniente prestar mucha atención a la manera en que muestraVariables define variables. La variable miColor está definida usando var, mientras que miFondo no la usa. De hecho, cuando una variable es definida sin utilizar var dentro de una función, tras bambalinas esto es como Javascript lo interpretaría:

function muestraVariables() {
 alert( miAltura + ' y ' + miAncho);
 var miColor = '#000000';
 window.miFondo = '#FFFF00';
}
Cada variable que no se defina usando la instrucción var en el contexto de una función, será asignada al objeto window y por lo tanto se convierte en una variable global.

Una variable definida dentro de una función con la palabra var se vuelve inaccesible desde el exterior, y es por ello que podemos decir que efectivamente se convierte en una variable privada.

También es posible crear funciones dentro de otras funciones:

function muestraVariables() {
 var miColor ='#000000';

 var miOtraVariable = function () {
     return miColor;
 }

 alert( miOtraVariable() );  // "#000000"
}

// fuera de la función
alert( miOtraVariable() );   // ¡Error!

Las variables definidas dentro de muestraVariables son inaccesibles fuera de ella, aunque miOtraVariable guarde una referencia a una función. Sin embargo, podemos ver que la función anidada si tiene acceso a miColor, ya que la función misma está definida dentro de muestraVariables.

Por lo anterior, podemos concluir que se debe evitar colocar variables e incluso funciones en el ámbito global, ya que por ejemplo, si se usa el mismo nombre de variable o función en dos archivos JS distintos, el último de ellos “sobreescribe” las definiciones del anterior, lo que en un momento dado implica que ¡Es posible que no estemos trabajando con las funciones o variables que pensamos!

En Javascript, el último en llegar gana.

En realidad, esto no es muy relevante si solo usamos una o dos funciones en una página web, pero cuando tenemos que trabajar en un equipo de desarrollo, cuando utilizamos o tenemos que convivir con código de terceros o cuando incorporamos bibliotecas externas en nuestra aplicación y si todos ellos colocan sus variables y funciones en el contexto global es posible que tengamos un mal rato tratando de entender porqué no obtenemos los resultados que esperamos, ¡Pensando incluso que se trata de un bug en nuestro código!

Alcance de nivel de bloque

En Javascript no existe el ámbito de variables a nivel de bloques, por lo que debemos estar atentos al hecho de que cualquier variable definida dentro de un bloque de código (for, while, if, switch, etc) será accesible desde el ámbito inmediato superior (siguiendo las reglas explicadas anteriormente con respecto al uso de var).

if( miAncho == 100) {
   var miAltura=200;
}
alert( miAltura ); // "200"

Esta es una diferencia fundamental con respecto a Java, en el cual el tiempo de vida de una variable termina al cerrarse el bloque que la contiene.

for(var i=0; i<=100; i++) {
   miColor=i;
}

for(; i<50; i++) {
   miAncho=i;
}

alert(miColor); //100
alert(miAncho); //error

Al definir la variable i con la palabra reservada var no se le da alcance de bloque dentro del ciclo for. Por lo tanto, al entrar al siguiente ciclo, el valor de i es 100, por lo que nunca se entra al segundo ciclo.

Esto no significa que no debamos definir variables dentro de bloques. Es importante que las variables se declaren lo más cerca posible del lugar en el que se usarán para aumentar la claridad de nuestro código. En este aspecto, es similar a utilizar Dim en Basic para declarar una variable dentro de un IF o un WHILE. Aunque posición de la declaración no cambia la semántica de la misma, si tiene un efecto en el potencial de comunicación de esta.

Objetos, Funciones y alcance

Hemos visto que la palabra var solo tiene significado dentro del contexto de una función, lo que nos habilita a tener variables privadas y globales si así lo deseamos. Sin embargo, las funciones nos posibilitan un nivel más de alcance: el “nombre” de la función.

Hemos visto que todas las variables definidas dentro de una función sin la instrucción var son asignadas al objeto window. Ok, pero digamos que queremos escribir código limpio y evitar el uso de variables globales, ¿Cómo podemos hacerlo?

De hecho, fuera de una función, en el ámbito global, podemos usar otro método para referirnos al objeto window mediante otra palabra reservada: this.

La palabra this tiene un papel muy importante en Javascript, pero al mismo tiempo es una fuente de confusión cuando se comienza a programar en este lenguaje.

Poniéndolo de forma llana, this siempre “apunta” al contexto actual en el que se está ejecutando nuestro código. Veamos por ejemplo:

miColor = 100;

alert( this.miColor );    // 100
alert( window.miColor );  // 100
alert( window === this ); // true

Como podemos ver, this en el ámbito global hace referencia al objeto window.

Por otro lado, las funciones también nos permiten utilizar this, pero en este caso, this no se referirá al objeto window. ¿Entonces a qué?

Como se mencionó anteriormente, todas las funciones están siempre asociadas a un objeto. Si la función se declara globalmente, entonces están asociadas al objeto window; si son “métodos” de un objeto, entonces están asociadas a ese objeto. Y this siempre apuntará al contexto dentro del cual se ejecuta una función, por lo que dependiendo de la forma en que llamemos a una función, puede afectar el contexto al que apunta this:

function miFunc() {
  alert( this === window );
}
miFunc();     // true
new miFunc(); // false

En este caso, this está apuntando a algo más que no es el objeto window. Para entender qué es ese algo, veamos cómo interpreta Javascript la última linea del ejemplo:

new miFunc();
 temp = new Object();

 temp.miFunc = miFunc;
 temp.miFunc()

Cuando se usa una función para inicializar un objeto (usando la palabra new, se dice que esta función es el constructor de ese objeto y el nombre de esa función se convierte en la clase del objeto.

Podemos comprobar que this se refiere a un objeto con el nombre de la función utilizando el operador instanceof, que nos dice si un objeto es de una “clase” específica o bien la propiedad constructor:

function miFunc() {
  alert(this instanceof miFunc);
  alert(this.constructor == miFunc);
}

miFunc();     // false, false
new miFunc(); // true, true

Todo esto puede sonar un poco confuso. Trataré de explicarme: Al usar la palabra reservada new se crea un objeto temporal en el background por nosotros, mismo que se asociará al nombre de nuestra función. Pero, ¿para qué nos sirve esto?

Bueno, en primer lugar nos habilita a utilizar un nivel más de alcance, el de objeto, que es similar al concepto de variables de instancia de los lenguajes orientados a objetos tradicionales. Con esto tenemos 3 niveles distintos:

function pruebaAlcance() {
    global = 100;

    var privada = 200;

    this.publica = 300;

    this.metodoPublico = function() {
         alert( 'puedo ser usado desde el objeto pruebaAlcance' );
    }
    var metodoPrivado = function() {
        alert( '¡No puedes llamarme desde el exterior!' );
    }
}
pruebaAlcance();

alert( global ); // 100

alert( privada ); // error

alert( pruebaAlcance.privada ); // undefined

alert( pruebaAlcance.publica ); // 300

alert( publica ); // undefined

alert( pruebaAlcance.metodoPublico() ) // 'puedo ser... etc'

alert( metodoPublico() ) // error

alert( pruebaAlcance.metodoPrivado() ); // error

alert( metodoPrivado() ); // error

Los niveles de alcance de Javascript pueden parecer extraños en un principio y puede costar trabajo acostumbrarse a ellos en un principio, pero también pueden ser una herramienta poderosa al momento de construir bibliotecas o convivir con código de terceros sin pisar los dedos de nadie más.

Las diferentes formas de declarar variables o métodos dentro de una función u objeto es la base mediante la cual se pueden simular espacios de nombres (namespaces) en Javascript y son la base de las técnicas orientadas a objetos del mismo.

Espero haya sido de su interés.

Este post fue publicado originalmente el 30 de Septiembre de 2008 en un blog anterior y posteriormente perdido en un crash de servidor.

23 de septiembre de 2009

Antipatrón: Requerimientos lanzados sobre la pared

Siguiendo con la discusión sobre requerimientos, recordé que alguna vez leí sobre el antipatrón llamado "Requiremens Tossed Over The Wall".

Básicamente el contexto es el siguiente:

Al equipo de desarrollo se le da una visión de muy alto nivel sobre el sistema y se le pide (como parte del "proceso", claro está) que desarrolle una propuesta completa y detallada. El experto del lado del negocio revisa la propuesta y hace varios comentarios sobre algunos errores obvios. Si los desarrolladores tienen alguna duda durante la fase de construcción, se concertará una cita para una reunión o, dado que las reuniones son asuntos relativamente caros, elaboran una lista de preguntas que son enviadas via e-mail. El experto eventualmente responderá a las dudas del equipo cuando tenga algo de tiempo disponible. Finalmente, el sistema es entregado y el cliente trata de utilizarlo, pero informa al equipo que hay varios "errores" que deben ser corregidos.

¿Suena conocido? Bueno, no es de extrañar, ya que de hecho es la forma en que se desarrollan la gran mayoría de los proyectos. Pero, ¿qué onda con el nombre raro? Bueno se debe a que mayormente, el desarrollo de requerimientos cesa en el momento en que la codificación comienza. Simplemente, los lanzo por encima de la pared y me olvido de ellos: "ya no es mi problema".

Cruelmente, la realidad suele ser bastante distinta. En realidad, esta forma de pensar asume que los requerimientos del sistema son de hecho estables, o que permanecerán estables el tiempo suficiente como para entregar el sistema terminado. A menos que trabajemos en un dominio en el que los requisitos dependan más del ambiente físico (el sistema de aviónica de un 747, por ejemplo) que del cambiante mundo de los negocios, generalmente las cosas no son así.

En cuanto al usuario, no quisiera adoptar una postura radical como la expresada en la primera edición de "Extreme Programming Explained: Embrace Change" de Kent Beck, en la que básicamente ベック先生 nos dice que todos los proyectos deben tener a un usuario del lado del negocio asignado al 100% durante la duración del mismo, y trabajando en el mismo lugar que el equipo. Si bien es cierto que eso sería lo deseable, en muchos casos no es posible, ya sea por una verdadera imposibilidad operativa o bien por cuestiones de idiosincrasia. Sea cual fuere la razón, incluso el mismo Beck ha flexibilizado su postura al respecto con el tiempo.

Lo que no ha cambiado en lo absoluto es el hecho de que una gran parte de responsabilidad sobre el éxito o fracaso del proyecto recae en que el usuario se involucre directamente en él. Muchos usuarios hoy en día siguen creyendo que el desarrollo de software es como mandar hacer un closet, para lo cual basta elegir un modelo del muestrario del carpintero, que éste último tome medidas y esperar un número de días acordado para recibir aquello por lo que pagué.

Seamos realistas. Si este fuera el modelo a seguir, todos los usuarios podrían irla llevando solamente usando software COTS. Sin embargo en el mundo real, las cosas no son así. La mayoría de los negocios tienen particularidades que los hacen únicos. Los gerentes y directivos aplican estrategias que los diferencian de su competencia. Sus políticas son diferentes. Sus procesos son diferentes. No importa cuantos sistemas de control de producción se hayan programado, jamas se hacen dos iguales. Si, hay una gran área común entre sistemas para diferentes empresas dentro de una misma industria (y es aquí donde la experiencia ganada en una ventaja), pero finalmente quien realmente sabe cómo llevar el negocio es... si, el usuario.

En México tenemos un proverbio muy folclórico, pero muy atinado:

A ojo del amo crece el caballo.

Si de este sistema depende el poder aprovechar una oportunidad de negocio o no, el poder atacar un problema recurrente en mi organización o dejarlo como está, el obtener información fidedigna hoy para poder tomar las decisiones correctas sobre el rumbo a tomar mañana, ¿no debería entonces yo por lo menos dedicar por lo menos una parte de mi tiempo o la de algún colaborador que conozca el negocio lo suficientemente bien, para asegurarme que las personas que van a desarrollar el proyecto hayan entendido correctamente mis necesidades? ¿que hayan entendido cual es el problema que se pretende atacar, el valor de negocio que se pretende obtener?

Esto no solo disminuye sensiblemente el riesgo de llegar 6 meses después a la fecha de entrega para darnos cuenta de que el sistema está mal, sino permite aprovechar la experiencia y el talento del equipo de desarrollo para obtener soluciones más creativas que las que inicialmente podríamos haber imaginado.

El valor real de los requerimientos bien levantados y desarrollados es que dan la oportunidad al equipo de desarrollo de expresar en el lenguaje del usuario cuales son sus necesidades y expectativas, y así asegurarse de que las ha comprendido correctamente. Los requerimientos escritos (especialmente los casos de uso o "especificaciones funcionales") son antes que nada una herramienta de comunicación:

  • entre el usuario y el equipo de desarrollo
  • entre miembros del equipo de desarrollo
  • entre miembros actuales y futuros del equipo
  • entre miembros actuales y futuros del negocio

Si, la comunicación directa y frente a frente es la más efectiva, pero los requerimientos pueden ser también altamente efectivos cuando se usan correctamente y se desarrollan por las razones correctas; esto es, cuando aportan valor al proyecto.

Y por el amor de Dios... no pidamos documentar 400 páginas de requerimientos que nadie piensa leer con la única justificación de que "el proceso lo exige".

17 de septiembre de 2009

¿Casos de uso ágiles?

Pues si. Mi última aventura ha involucrado el delicioso asunto de documentar casos de uso que nadie va a leer jamás. O por lo menos no lo suficiente como para justificar el esfuerzo requerido en ello.

No me malinterpreten. En realidad yo no creo que toda la documentación, el levantamiento de requerimientos y la elaboración de casos de uso sea una perdida de tiempo. Pero si lo son cuando se elaboran exclusivamente para "cumplir con el requisito".

But I digress... lo que realmente quiero comentar aquí es la manera en que atacamos el problema de la documentación de requerimientos. Una de las partes más frustrantes del proceso es la casi infinita variabilidad en cuanto a los formatos "oficiales" que manejan las empresas para estos menesteres. Es verdaderamente desmotivante el hecho de tener que hacer un cierto trabajo, querer hacerlo bien (porque por más que nos desagrade, aún tenemos nuestra ética) y descubrir que no puedes avanzar como quisieras por que tienes que estar lidiando con las idiosincrasias del procesador de palabras, en lugar de poder concentrarte en lo que realmente importa de la documentación (aunque muchas personas lo duden): el contenido.

La solución que encontramos es utilizar una tecnología a la que desde hace varios le tengo un especial cariño: XSLT (Transformaciones del Lenguaje Extensible de Hojas de Estilos).

La idea es definir un esquema sencillo XML para el documento principal (el contenido) y utilizar XSLT y CSS para formatear el documento final. Nuestro esquema tenia elementos como <caso>, <flujoPrincipal>, <paso>, etc. Esto nos permitió olvidarnos durante la mayor parte del tiempo del formato del documento y concentrarnos en escribir casos de uso que fueran claros y tan completos como fuera posible. Además, ¿no es eso lo que predican las reglas del buen diseño?

Desacoplar el modelo de las vistas.

O como lo dirían los diseñadores Web:

Separar el contenido y el formato.

Una gran ayuda fue la utilización de Komodo Edit para la edición del documento HTML, ya que este editor permite tener una vista previa del documento final y tiene una función de corrección de ortografía muy útil. Como utiliza los mismos diccionarios que Firefox y Thunderbird, es muy sencillo de utilizar y extender.

Otra herramienta que fue invaluable fue el programa Prince XML, que permite generar un documento en formato PDF a partir de un XML o HTML. Nos dio un gran control sobre el layout de la página con sus extensiones para CSS. Verdaderamente una herramienta fantástica.

Finalmente, el que para mi es la estrella de la película es GNU make. Seguramente que alguien se podrá preguntar "¿Qué? ¿make? ¿Qué eso no es para programadores en C?". Bueno, si lo es. Pero no necesariamente debe tratarse de un proyecto en C para que make sea útil. En realidad, Prince es una herramienta genial, pero cuando tienes que generar un documento PDF de alrededor de 380 páginas, el proceso se vuelve algo lento.

El poder de make para verificar dependencias y generar (o regenerar) partes o artefactos de forma automática, es un lujo que muchos programadores de hoy en día nunca han tenido y muchos programadores con algunos años de experiencia ya no recuerdan. Lo que es más, nos permitió validar nuestros documentos en tiempo de "compilación". Al momento de aplicar la hoja XSL, nuestro makefile hace una llamada a msxls para validar el documento contra el esquema. Eso por sí mismo nos ahorró muchos problemas (en especial cuando el documento comenzó a crecer y tanto Komodo como Stylus Studio se comenzaron a poner lentos). Además antes de pasar el resultado a prince, make lo pasaba primero a través de tidy para verificar el código generado por nuestra hoja XSL.

Un detalle que pasamos completamente por alto y que pudo ser de gran ayuda fue el uso de XSL-FO (XSL Formatting Objects). La primera vez que escuché de esa tecnología todavía estaba muy cerca del "bleeding edge" como para considerarla seriamente, pero parece que hoy en día se encuentra mucho más madura. Tendré que hacer una nota mental para estudiarla seriamente un día de estos

Mientras que sigo dudando que el proceso de documentar casos de uso se vuelva alguna ves una de mis actividades favoritas, con algunas de las cosas que aplicamos en este proyecto, me parece que no debe ser tan oneroso después de todo.

9 de septiembre de 2009

Antipatrones

Steven R. Covey menciona en su libro "El poder de los 7 hábitos" que una manera efectiva de estudiar algo es estudiar su opuesto.

Bueno, mientras que al tema de los patrones (especialmente los de diseño) se le ha dedicado una gran cantidad de "lip service" durante los últimos años, un tema paralelo y en mi opinión, igualmente importante, es el de los antipatrones.

¿Qué es un antipatrón?

Un antipatrón es una solución común que se da a problemas recurrentes en un contexto dado, que trae consecuencias negativas para el proyecto en el que se aplican.

En otras palabras y como decimos en México: el remedio es peor que la enfermedad.

¿Porqué es importante el estudio y difusión del conocimiento en antipatrones? ¡¡Porqué desgraciadamente son mucho más comunes de lo que deberían ser!!

Estas "soluciones", son consideradas como alternativas viables por muchos profesionales e incluso, algunos han hecho de ellos una forma de vida.

Para muestra basta un botón:

Programación de copiar y pegar (copy and paste programming):
Programar copiando y modificando código existente en lugar de crear soluciones genéricas.

Ahora bien, todos lo hemos hecho, ¿o no? y todos deberíamos saber a estas alturas las consecuencias de hacerlo de manera indiscriminada durante algún periodo de tiempo:

  • Redundancia innecesaria
  • Mayor complejidad inherente
  • Pobre mantenibilidad
  • Alta propensión a errores, etcétera

Sin embargo, hace pocos años tuve una compañera de trabajo que a los nuevos programadores que entraban a su cargo los adiestraba con su "metodología" de desarrollo, más o menos así:

Bajas el módulo X del control de versiones y haces una copia. Le cambias el nombre al módulo y lo pegas en el proyecto donde lo vayas a ocupar. Después te copias el texto del módulo "globales" del proyecto original y lo pegas todo en el "globales" de tu proyecto y comentas todas las variables. Después comienzas a descomentarlas una por una hasta que compile bien.

Lo que intento decir es que algunos de los problemas que describen los antipatrones son cosas que hacemos todos los días sin darnos cuenta de las consecuencias que traerán al proyecto. Algunas las hemos hecho desde que aprendimos a programar. Algunas nos fueron enseñadas explícitamente por nuestros maestros y otras han estado con nosotros desde los albores de la programación.

La única forma de detener la diseminación de estas malas prácticas es la educación, tomando responsabilidad de nuestra propia práctica como profesionales.

En futuras entradas, tengo la intención de discutir con mayor detalle algunos de los antipatrones conocidos. Hasta entonces, les dejo algunos links:

8 de septiembre de 2009

Presentación

Antes de comenzar, quiero contestar una pregunta que seguramente alguien que pasa por esta página se pueda hacer: ¿Porqué necesitamos otro blog sobre programación?

Bueno, la respuesta que tengo es algo ambigua: No lo necesitamos. O por lo menos no creo que nadie en el mundo necesite mis opiniones al respecto. Esto es algo que me gustaría dejar bien claro. No quiero asumir que poseo la verdad absoluta sobre algún tema o presumir que soy más inteligente que nadie más o que los lectores que pudiesen leer este blog sean más tontos que yo. Esa clase de blogs hay muchos y muy variados allá afuera.

Más bien, lo que pretendo (algo así como la "visión" para este blog) es solamente compartir mis experiencias al aprender y aplicar lo aprendido sobre desarrollo de software.

Durante años (me inicié en este asunto por allá de de 1989) creí que sabía bastante sobre programación de computadoras. Conocía varios lenguajes, entre los que se incluían Pascal, C++ y ensamblador. Más aún, en su momento aprendí a utilizar las extensiones de objetos de Pascal y C++ y creía que lo entendía perfectamente: son estructuras de datos con sub-rutinas integradas.

Bueno, todo mi mundo de sueños y la ilusión que tenía se vinieron abajo cuando llegó a mis manos una copia de "Refactoring: Improving the Design of Existing Code" de Martin Fowler. Ese libro cambió mi vida. No solamente me dio una perspectiva totalmente nueva de lo que yo creí que sabía, sino que me hizo darme cuenta que no entendía ni una palabra de lo que era la Programación Orientada a Objetos... y que la gran mayoría de los libros, profesores, cursos y colegas programadores que había conocido a través de los años, ¡Tampoco lo entendían!

Al poco tiempo y de nuevo, gracias a Mr. Fowler, descubrí los Patrones de Diseño, los Métodos Ágiles y a figuras enormes (como Kent Beck, Ward Cunningham, Robert C. "Uncle Bob" Martin, entre otros) que poco a poco transformaron mi forma de ver y entender el desarrollo de software.

Hoy, casi 10 años después de aquel momento "¡¡Ajá!!", sigo aprendiendo y sigo intentando aplicar a mi trabajo de todos los días, lo poco o mucho que he absorbido de esos gigantes a los que yo humildemente llamo "私の先生" (mis maestros), mis "role models".

Este blog es solo un intento de relatar los éxitos y frecuentemente, los fracasos en esa búsqueda. Espero que mis amigos que se me han unido en mi obsesión me acompañen nuevamente en esta nueva aventura y que ud. amable lector, encuentre algo de interés y cómo no, me ayude con sus comentarios, dudas, críticas y correcciones.