Programación de interfaces basada en componentes

O de la composición de interfaces mediante un lenguaje de programación, no un lenguaje de marcado.

En mi travesía por aprender cómo mejorar lo que hice en mi último proyecto, estoy empezando a apreciar el encaje que tienen ideas como el immediate mode, las funciones puras y la inmutabilidad. Conceptos que transcienden modas y que, al entender su utilidad y trade-offs, se introducen en el conjunto de herramientas que tenemos a nuestra disposición, sea cual sea el contexto en que programamos.

Hay otro concepto al que recientemente le estoy prestando atención: programación de interfaces basada en componentes.

¿En qué consiste?

En la creación de elementos reutilizables que sean autosuficientes. Es decir, estables por sí mismos y que no dependan de estado global.

components_before components_after
Lo que hacemos ahora Lo que necesitamos hacer

Es muy interesante comparar cómo diversas herramientas proponen la creación (o no) de componentes. La selección tecnológica es, en sí mismo, un tema con muchos matices y tonalidades y existen diversas aproximaciones para comparar toolkits de programación. Algunas aportan algo, otras no y otras depende.

Por ejemplo, a la hora de comparar dos toolkits líderes de sus respectivos sectores como Wicket y React podríamos hacerlo de la siguientes maneras:

Sin embargo, si los comparamos en términos de cómo componen la interfaz, vemos que su aproximación es similar: proponen crear componentes que encapsulen conjuntamente HTML, CSS y JavaScript. ¿Cómo lo hacen? Tanto Wicket como React crean los componentes mediante un lenguaje de programación (Java/JavaScript) y no mediante un lenguaje de marcado (HTML).

  • Componente en Wicket. En Wicket, la unidad mínima de reutilización es el Panel, que consiste en varios archivos: panel.java, panel.html y opcionalmente otros de localización como panel.properties.
  • Componente en React. En React, el componente es un archivo JavaScript que devuelve código HTML. JSX es únicamente azúcar sintáctico que ayuda a que el código JavaScript sea más expresivo.

Esta aproximación los diferencia de otros toolkits como Angular, Polymer, JSP o Mako, que serían ejemplos de lo contrario: la composición de la interfaz se hace mediante un lenguaje de marcado -HTML- o derivativos que compilan a él.

¿Esto supone una mejora?

La respuesta rápida: sí, porque en un lenguaje de programación tienes a tu disposición 50 años de investigación en computer science, destilados con más o menos suerte. Hombros de gigantes sobre los que mirar más lejos.

La respuesta más elaborada: las interfaces son sistemas complejos, necesitamos subcomponentes para simplificar su creación y mantenimiento. Hay dos áreas donde un lenguaje de programación supera al de marcado para crear subcomponentes: encapsulación y expresividad.

Encapsulación

La encapsulación consiste en la creación de elementos que podamos (re)usar sin la obligación de entender sus propiedades internas, ni de empezar todo desde cero cada vez. En un lenguaje de programación tenemos herramientas para encapsular elementos y funcionalidades como paquetes, módulos, clases, funciones, herencia, mixins, patrones de diseño, etc. Por el contrario, en un lenguaje de marcado como HTML, las opciones son inexistentes.

Iniciativas como WebComponents se han creado 20 años después del propio HTML. Son bienvenidas, pero no podemos obviar el elefante en la habitación: sólo nos ofrece la creación de paquetes, no todo lo demás. En Wicket y React los componentes son elementos que están programados en Java/JavaScript y, por lo tanto, podemos hacer con ellos lo que normalmente haríamos con cualquier otro trozo de código: herencia, composición, aplicar patrones de diseño, etc.

Expresividad

La expresividad consiste en la capacidad de programar los distintos matices que deseamos. En un lenguaje de programación tenemos a nuestra disposición mecanismos como tipos de datos, control de flujo, inversión de flujo, bucles, paso de mensajes, etc. HTML no tiene nada de esto.

Los toolkits que pretenden componer mediante un lenguaje de marcado -JSP en Java, Mako en python, etc- no poseen esa expresividad. Para solventarlo, tratan de integrar parte de ella en un lenguaje propio que compila a HTML: un sistema de plantillas. Un ejemplo típico que casi todos los sistemas de plantillas poseen son algunas construcciones para controles de flujo y bucles. Por ejemplo, en JSP:

<c:when test="${isThisVariableTrue}">
 <h1><fmt:message key="Title" /></h1>
 <c:if test="${isThisOtherVariableTrue}">
 <fmt:message key="showMessage" />
 <c:out value="${value}" />
 </c:if>
 <c:if test="${isThisOtherSecondVariableTrue}">
 <fmt:message key="aDifferentMessage" />
 <c:out value="${aDifferentValue}" />
 </c:if>
</c:when>

También necesitan crear mecanismos para pasar información del código a la plantilla y suelen ofrecer nuevos tags HTML para realizar acciones que HTML no permite. Interacciones que cualquier lenguaje de programación incluye por defecto pero que un sistema de plantillas integra con esfuerzo, limitaciones y a costa de aprender una nueva sintaxis no reutilizable en otros contextos. Para controlar la complejidad necesitamos más expresividad que la que nos aportan bucles y condicionales. Si lo único que puedes utilizar es un martillo, todos los problemas te parecerán clavos.

Conclusión

Sería simplista decir que Wicket y React se han convertido en líderes de sus respectivos sectores únicamente por la propuesta de creación de interfaces mediante componentes. Es, sin embargo, un fundamento que comparten y plausible para explicar por qué React tiene éxito y Polymer no: como la productividad aumenta al usar esta aproximación, se acaba extendiendo por microdecisiones de agentes interrelacionados que buscan su propio beneficio.

Al pivotar la construcción de componentes sobre un lenguaje de programación y no sobre un lenguaje de marcado tenemos a nuestra disposición todas las herramientas de encapsulación y expresividad disponibles en el lenguaje, lo que facilita domar la complejidad inherente a la creación de interfaces. El aumento de productividad es de órdenes de magnitud.

Para entender en toda su complejidad los efectos del cambio, conviene releer parábola de los relojeros.