Tinselcity

Nota: Lo siguiente describe cierta práctica encontrada en el contexto del Proyecto ¡Ay, ay, ay, Agile!. Debe entenderse que no es en absoluto recomendable hacerlo como se hace en ese proyecto. Por eso esta explicación.

Indirecciones

Muchas veces se dice que los programadores abusamos de la indirección. Existe, incluso, un dicho popular que señala que “Todos los problemas en programación se pueden resolver añadiendo otro nivel de indirección más”. Es el “Teorema Fundamental de la Ingeniería de Software,y aunque a veces se le añade de forma humorística ”…excepto el problema de tener demasiados niveles de indirección“ porque todo en exceso termina siendo un problema en sí mismo, la indirección es una herramienta fundamental para gestionar la complejidad de los sistemas.

La idea es sencilla y aplicada extensamente en casi todos los contextos. Se trata, simplemente, de referirnos a las cosas de forma indirecta. Es decir, en lugar de hacer referencia a cierta entidad (un dato, un nombre, un objeto, lo que sea), damos un nombre a esa referencia y usamos este para referirnos a la entidad final. Dicho así parece más complicado de lo que es, pero la idea puede ser tan simple como usar una variable para referirnos a cierto valor.

Complejidad

La complejidad toma muchas formas y se podría hablar mucho -y de hecho se ha hablado ya mucho- sobre ella. La complejidad se gestiona principalmente con estructura y orden. Uno de los dos principios que personalmente creo que deben guiar a los programadores en todas sus decisiones de arquitectura, decisiones de estructura y orden, decisiones en el fondo de gestión de la complejidad, es el que no me canso de repetir: “Juntar lo que va junto; separar lo que va separado”.

Y es justo ahí donde la herramienta de la indirección nos ayuda: separando lo que va separado. Es decir, la indirección es una herramienta que nos permite separarnos de algunos detalles que no son relevantes o que no queremos gestionar “en este lugar”. Nos permite llevar ese detalle a otro sitio y desde aquí hacer referencia a él de forma indirecta.

Como he dicho, la indirección es por tanto una herramienta fundamental en el diseño de sistemas y en el desarrollo de software en general en prácticamente todos sus detalles.

Literales

Un aspecto del desarrollo al que todos los proyectos de un cierto tamaño tienen que enfrentarse antes o después es el de los textos. Desperdigados por todo el interfaz del programa van apareciendo pequeños -o no tanto- textos, etiquetas de campos, botones, títulos, y en general todo tipo de textos. Todos esos pequeños “Nombre:”, “Aceptar”, “Cancelar”, “Código de Unidad”, “Fecha de Nacimiento”, y un inacabable etcétera de pequeños literales que indican al usuario qué significa cada elemento de la interfaz.

A veces, dependiendo del proyecto, es necesario poder presentar esos textos en diferentes idiomas. Otras veces no es necesario. Pero en cualquier caso, es interesante introducir aquí un nivel de indirección que nos ayude a gestionar las complejidades de estos literales. Normalmente el problema de gestionar estos literales es de una complejidad baja. Las necesidades que tenemos sobre ellos son sencillas: cambiar uno por otro, o cambiar muchos de golpe. Pero eso que comentaba de encontrarlos desperdigados por todo el interfaz, hace que esos cambios puedan llegar a tener un coste relativamente alto, ya que nos obligan a buscarlos por toda la aplicación.

Así que, como es natural, existen ya soluciones bastante establecidas y, como decía, se basan en introducir un nivel de indirección entre los valores concretos de los literales (e.g. “Código de proveedor”) y su uso en todos los lugares donde aparece. No descubro nada nuevo con esto; es algo bastante establecido ya. En el código fuente de una aplicación web típica podríamos encontrar algo como esto:

Cuando esto se presenta al usuario, lo que este verá será “Usuario:” y “Contraseña:” ahí donde dice <bean:message key=“login.form.username”/> o <bean:message key=“login.form.password”/>. O a lo mejor verá “ID:” y “Clave:” o “User:” y “Password:”. Ahí está la gracia. Tenemos un mecanismo para poder cambiar qué literal concreto queremos que aparezca en cualquier sitio donde aparezca <bean:message key=“login.form.username”/> en el código fuente del interfaz. Luego, en un único lugar tenemos guardados los valores concretos de los literales:

login.form.username=Nombre del usuario
login.form.password=Contraseña

No descubro nada nuevo con esto. Como digo es un problema muy conocido y las soluciones están bastante establecidas y claras. Aún así, es interesante pensar un poco en ello y entender por qué hacemos esto.

Cambios

Hay dos ventajas principales que nos da el uso de esta solución. Una es la consistencia. Si tenemos varios sitios de la aplicación donde hacemos referencia, por ejemplo, al “Código del Usuario Destinatario”, querremos que siempre aparezca escrito así, y no que unas veces ponga “Id de Destinatario”, otras “Código de U. Receptor” y otras “Clave del Destino”. La consistencia hace que el usuario de la aplicación pueda comprenderla más fácilmente. Si siempre nos referimos al mismo concepto con las mismas palabras, el usuario puede identificar ese concepto sin ambigüedad. Si no, tendrá que andar adivinando si nos referimos al Código de Usuario Destinatario o a otro concepto similar pero diferente.

La segunda es una ventaja a la hora del propio desarrollo. Es decir, es algo que nos ayuda a nosotros, a los programadores de la aplicación. Esta ventaja es que hemos centralizado todas las apariciones de un mismo literal en un único sitio. Si en un momento dado surge la necesidad de cambiar una expresión o una palabra por otra por decisiones del negocio, podremos realizar el cambio en un único lugar: En ese fichero de literales que tenemos. (No tiene por qué ser un único fichero, claro. Podemos organizarlo en varios ficheros según el idioma, o lo que necesitemos)

Implementación

No voy a explicar cómo funcionan este tipo de soluciones; no es algo demasiado interesante y ya está bastante resuelto. Pero sí es interesante ser conscientes de que incluso una solución ya establecida y relativamente sencilla, se puede utilizar mal. En concreto, es importante saber que podemos utilizar la indirección tan mal que perdamos todas las ventajas que nos proporciona.

En el proyecto en el que, lamentablemente, me encuentro trabajando, mañana me espera una tarea de cambio de literales. El cliente ha solicitado algunos cambios sobre las expresiones que se usan para referirse a ciertos conceptos de los números de lote y números de serie. En total hay 38 literales afectados y si todo estuviera bien hecho, el cambio implicaría, simplemente editar el fichero de literales y cambiar esos 38.

Sin embargo, el proyecto utiliza los literales de esta forma:

label.numeroDeLoteYSerie=Número de Lote y Serie
label.introduzcaFechaDeCaducidadDelLote=Introduzca Fecha de Caducidad del Lote

El problema, como se puede observar, es que no hay indirección real. Se está usando como identificador, como referencia, el mismo valor al que nos referimos. Lo mismo que pone a un lado de la separación, lo ponen al otro lado. Y así, claro, no existe tal separación en absoluto.

La tarea que veré mañana pide que cambie no solo treinta y tantos literales, sino unas 750 apariciones de esos literales en 398 pantallas de la aplicación.

Y todo ello se ve normal, porque nadie en el proyecto entiende para qué sirven herramientas tan básicas como la indirección. Se introduce la herramienta, se aplica mal, y se sigue pagando el mismo coste que si no se utilizara o incluso más.

Soluciones

El problema anterior es más común de lo que puede parecer. No ya en el uso de literales y etiquetas, sino en general en el nombrado de cualquier referencia. Es el mismo problema que encontramos, por ejemplo, cuando alguien da nombre a una variable no por lo que representa sino por el valor que tiene. En un caso muy extremo y muy estúpido, pero lamentablemente real, comentaré que he visto cosas del estilo de:

const TRES = 3;

// ... más adelante ...

if (total > TRES) throw new Error ("El máximo aceptado es tres");

Es decir, se da nombre a la variable -o constante en este caso- según el valor que tiene en un momento dado del desarrollo (3, TRES), en lugar de pensar en lo que representa (MAXIMO_ADMITIDO).

La solución a estas alturas es obvia, porque se trata exactamente de eso: Dar nombres a las referencias por lo que representan.