miércoles, noviembre 12, 2008

Antología del Disparate - III

Advertencia: Cualquier parecido con
la coincidencia, es sólo una sospechosa irrealidad...
Primero uno resbala...uando, meses atrás, se me ocurrió la idea de escribir sobre el cargocultismo estaba pensando, en particular, en el problema artificial, creado por mis amigos imaginarios, que voy a discutir a continuación. Repasemos la definición: el cargocultista es el individuo que practica determinados rituales técnicos porque sabe, o ha oído decir, que a otros les funcionan, pero sin comprender realmente cómo se supone que tienen que funcionar.
En el mundo de la programación, uno de esos rituales misteriosos tiene que ver con la creación de gestores especiales para distintos módulos de la aplicación, sobre todo cuando se trata de proyectos grandes. Una aplicación "decente" tiene que definir, por fuerza, un "gestor" de menúes, un "gestor" de excepciones, un "gestor" de formularios... y así hasta el aburrimiento o la bancarrota. Para ser exactos, una parte importante de esta secta de cargocultistas no utiliza la palabra "gestor" sino manager, que se supone que debe darle un poco más de dignidad al engendro.
... y luego, si no tienes cuidado, te la pegas.Como suele ocurrir en todos los casos de cargocultismo, la propia idea de un gestor de menúes y ventanas no es mala. ¡Todo lo contrario! Estos módulos suelen ahorrar mucho código repetitivo, y tienen la bonita propiedad de dejarse reutilizar fácilmente de un proyecto al siguiente. Incluso cuando se utilizan en un único proyecto, nos ofrecen la libertad de hacer cambios notables en la interfaz de usuario sin necesidad de retocar todo el código. Visual Studio, por ejemplo, nos permite elegir entre una aplicación MDI tradicional o una en la que los documentos aparecen en páginas paralelas accesibles por pestañas. Lo verdaderamente malo, es inventarse uno de estos gestores sin saber por qué, para qué o cómo.

Cuello de botella

A diferencia de disparates anteriores, esta vez el problema no está en una simple instrucción equivocada, sino en el diseño global de un módulo. Los problemas comienzan con la decisión, consciente o inconsciente, de esta gente de ignorar el mecanismo preestablecido para la traducción en .NET y de montar un barroco sistema basado en ficheros XML externos. El vínculo entre cada elemento de la interfaz y su correspondiente cadena se realiza manualmente. Supongamos que usted quiere añadir una etiqueta a un formulario que debe decir "Total". De acuerdo a la metodología de estos señores:
  1. Primero debe ocurrírsele un nombre global para identificar la etiqueta. No se les ocurrió generar ese nombre a partir del nombre del control y del formulario donde se encuentra.
  2. Luego tiene que ir a cada fichero XML de traducción y crear una entrada que traduzca el identificador al correspondiente idioma.
  3. Cada formulario tiene un método Traducir. No crea que se trata de un método virtual heredado: la herencia visual es algo desconocido en esta metodología. Tiene que añadir una instrucción manualmente a Traducir en la que asignará a la propiedad Text de la etiqueta, el resultado de una llamada explícita a un método que busca en un diccionario en memoria la traducción correspondiente.
Usted se dirá que... bueno, es bastante trabajo, claro... pero con un poco de disciplina... etc, etc. ¡Ah, cuán equivocado estaría! ¿No le he dicho aún que esta gente trabajaba con SourceSafe, para el control de versiones? Imagine lo que ocurre cada vez que dos programadores intentan programar dos formularios que nada tienen que ver entre sí: ¡se tienen que pegar por el acceso a los ficheros de idiomas!
De todas maneras, aunque no se produjese este increíble cuello de botella, el sistema es inaceptable:
  1. Microsoft ofrece, de serie y sin coste adicional, un mecanismo idéntico en funcionalidad y sin ninguno de estos problemas: ficheros de recursos y ensamblados satélites.
  2. El sistema de Microsoft no le obliga a añadir código manualmente en ningún momento.
  3. El sistema de Microsoft le permitiría, incluso, ahorrar trabajo suponiendo la existencia de un "idioma neutro", para el cuál no tendría que mantener una tabla de recursos adicional.
  4. El sistema de Microsoft es verificable en tiempo de compilación: si compila, funcionará. En cambio, el invento en cuestión utiliza cadenas como claves en el diccionario. Resulta imposible verificar automáticamente la existencia de errores de mecanografía. Por supuesto, si esta gente hubiese usado enumerativos para las claves, el problema se resolvería... pero los dioses ciegan a aquellos a quienes quieren destruir.
Moraleja parcial: no hay que reinventar la rueda, si además, va a rodar más despacio.

Escarnio de la locura

Pues bien, queridos amiguitos, hay también un menú diseñado de acuerdo a esta locura con sistema. Hay claves con formato de cadenas, que tienen que transmitirse desde el código al fichero externo y viceversa, hay versiones del menú para cada idioma soportado y todo lo demás. Pero me voy a centrar en un aspecto muy concreto: en cómo se maneja la ejecución de los comandos del menú.
Cada vez que se ejecuta un comando del menú principal, se invoca un manejador de eventos situado en la ventana principal. No crea que hay un manejador diferente para cada comando, lo cuál habría sido relativamente sencillo de conseguir: todos los comandos van a parar al mismo método. Y ese método, cómo no, contiene una inmensa instrucción switch, que toma la cadena de caracteres designada como clave del comando para ejecutar el código apropiado.
¿Y en qué consiste ese "código apropiado"? Maravíllese: en una llamada a un método global estático de una clase independiente, a la que llamaremos Acciones, que se encuentra en un gigantesco fichero C#.
  • Nuevamente, tenemos un fichero que se convierte en un cuello de botella.
Claro, los conflictos por el acceso al fichero de acciones sólo tendrían que surgir cuando se modificase la estructura del menú... ¿o no? Resulta que no: cuando esta gente tiene que mostrar una ventana que permite una sola instancia, incluye, en el fichero de acciones, un bucle explícito para buscar si existe ya una instancia del tipo de ventanas en una lista global. Si la encuentran, la activan. Y si no, la crean. Y este código se repite tropecientas y tantas veces, una por cada ventana de este tipo, en el fichero de acciones.
  • Con un poco de reflexión, el código común se podría haber reducido a una simple llamada.
  • Pero mejor aún, el switch original ni siquiera tendría que haber pasado por la clase Acciones, sino que tendría que llamar directamente a métodos estáticos de las clases de ventanas correspondientes. De esa manera, cualquier cambio en la forma de mostrar una ventana, se aislaría dentro del código de la propia ventana.
  • Para rematar, esta gente ni siquiera utilizó una metodología uniforme para la creación y activación. A veces llamaban a Show, otras veces combinaban BringToFront, Activate y Focus; a veces cambiaban la forma del cursor, y en otros casos, mostraban una ventana splash con el mensaje "Espere un poco...". Esto complica mucho limpiar el código.
Con todo, lo peor es que a estar alturas todavía les cuesta creer que este sistema sea un error: el cargocultismo susurró en sus oídos que necesitaban un "gestor de menúes" para su aplicación "seria", y ahora tiemblan cada vez que mis tijeras podan porciones cada vez mayores de este cadáver exquisito. Mientras limpio los fluídos putrefactos de mis instrumentos de corte, veo ojos asustados y oigo el coro de plañideras a mi alrededor. Sonrío tranquilizadoramente, pero compruebo que mi sonrisa parece cada día más siniestra.
Y es que uno termina hartándose, ¿sabe?

Etiquetas: , , ,

11 Comments:

Blogger Andrés Ortíz said...

Hola Ian

Tiempo sin leerte, que bueno que regresas con esta buena sobredosis de entradas que dan qué pensar.

Oyeme una pregunta salida del tema

¿Sabes cómo hacerle para ejecutar una aplicación que lea un archivo (plano, XML, etc), y se pueda ejecutar tanto en Windows como en MAC OS? Yo imagino una especie de página HTML con javascript que haga la lectura al archivo, pero mi incognita está en cómo funcionaría teniendo en cuenta a los señores MAC y Windows.

Saludos

lunes, noviembre 17, 2008 3:33:00 a. m.  
Blogger Ian Marteens said...

Hola, Andrés:

La respuesta depende de la "evolución" que siga ese fichero XML. Si se trata de un fichero XML que debe modificarse en el lado cliente, probablemente se complique el diseño por culpa de los permisos. Ese sería el "punto débil" y, antes de lanzarme por ahí, yo comprobaría si, efectivamente, puedo hacerlo sin problemas desde navegadores en los dos sistemas operativos.

lunes, noviembre 17, 2008 8:33:00 a. m.  
Blogger Alfredo Novoa said...

Además del cargocultismo hay otro fenómeno parecido que se llama "Buzzword Compliance", que consiste en encajar en los proyectos como sea todas las cosas que están de moda en ese momento.

lunes, noviembre 17, 2008 3:07:00 p. m.  
Blogger Ian Marteens said...

:) La penúltima era XML, ¿cuál es la más reciente? ¿lenguajes dinámicos? ¿duck typing? ¿tiene nombre de reptil, o de piedra preciosa?

lunes, noviembre 17, 2008 3:40:00 p. m.  
Blogger Alfredo Novoa said...

La última que he visto es la de estar en las nubes o algo así :-)

lunes, noviembre 17, 2008 3:48:00 p. m.  
Blogger Ian Marteens said...

Es cierto, madre mía. Es todo un fenómeno nebuloso...

lunes, noviembre 17, 2008 3:54:00 p. m.  
Blogger Andrés Ortíz said...

Ian

Siguiendo el tema de la app para MAC y Windows, qué opinas si se utilizara Flash. El engendro corre sin problema en los 2 sistemas. La app es solo lectura, leerse los registros del XML en una app tipo HTML sin muchos enredos. El tema es que todo funcione para el usuario final tan trasnparente al introducir su DVD en cualquiera de los sistemas.

Quedo atento, y otros comentarios les agradesco mucho

Saludos

martes, noviembre 18, 2008 9:43:00 p. m.  
Blogger Ian Marteens said...

qué opinas si se utilizara Flash.

Me parece bien. De todos modos, eso no aclara lo del XML.

También podrías usar Silverlight. Ahora, te advierto que no he hecho nada con Silverlight: no sé hasta qué punto ya es práctico trabajar con él.

jueves, noviembre 20, 2008 11:46:00 a. m.  
Blogger Pablo Grisafi said...

Bueno, hay algunos motivos válidos para hacer uno mismo un traductor de ventanas y controles y no usar el sistema de Microsoft: (y por eso yo lo tuve que hacer)
1 - Poder usar archivos de texto plano que le permitan al cliente añadir sus propios idiomas sin recompilar, ni usar el Visual Studio, ni el espantoso WinRes.EXE
2 - En Compact Framework, hasta hace poco, no se podía usar la herramienta de traducción. Y aún hoy no se puede tener una aplicación en un idioma independiente del idioma del SO.
3 - Mejorar la consistencia entre ventanas de la traducción : Si hay 5 botones que deben mostar el texto Crear Factura, quiero una única entrada
ButtonCreateInvoice=Crear factura
que sirva para todos.(Y tambien quiero poder cambiar ese texto en una ventana en particular con
FormParticularButtonCreateInvoice=CrearFact.
porque no tengo lugar físico)

Pero si, perder la verificación en tiempo de compilacion es una reverenda porquería...espero que Microsoft mejore un poco su sistema para poder usarlo....

miércoles, noviembre 26, 2008 8:28:00 p. m.  
Blogger Ian Marteens said...

perder la verificación en tiempo de compilacion es una reverenda porquería

Hay una solución parcial: usa un enumerativo para indexar el diccionario. El problema es que no se puede definir un "partial enum" en C#, y que al final tienes un enorme fichero con la declaración del tipo. Pero al menos tienes algún tipo de seguridad en el uso de las claves.

miércoles, diciembre 03, 2008 11:09:00 p. m.  
Blogger Pablo Grisafi said...

Bueno, yo uso un horrible archivo enorme con "public const string" que es casi lo mismo (creo, aunque ahora pienso que el enum me da la seguridad de nunca usar un string por hacerlo a las apuradas, aunque por otro lado la clase de strings si puede ser partial...), y trato de usar reflection para generar un archivo con las claves...y no me gusta nada tener un diccionario enorme en memoria, alguna vez he pensado en usar un weak reference, pero son todos parches horribles. No puedo creer que la solución oficial de M$ no prevea añadir archivos de traducciones sin recompilar. Estimo que debe ser por un tema de seguridad, pero es una limitación demasiado estricta. (Tenia la esperanza de que me dijeras algo así como "eres un burro, esto se hace con la herramienta Winres2").
Y una pregunta ¿cuanto mas lenta es la llamada a un método por reflection que la llamada "normal"? En un framework que hice una vez, en el MenuManager llamaba así a los métodos...era flexible, permitía que el administrador configure por base de datos que menúes estaban en cada pantalla, pero siempre pensé que era medio desprolijo, y que usando interfaces y algún patrón podría haber evitado el uso de reflection....pero es tan lindo usar reflection, queda tan "natural" para esas cosas...
¡Saludos y gracias por el blog y por volver a escribir!¡Nos habías abandonado!

miércoles, diciembre 03, 2008 11:28:00 p. m.  

Publicar un comentario

<< Home