in All, Español, How I work

Programación de interfaces basada en componentes

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.

Leave a Reply

  1. @nosolosw no me acaba, porque yo estoy recorriendo el camino inverso. Es por lo que han pasado los frameworks .Net y Qt, de querer hacerlo todo con programación y componentes. Han tenido que adoptar lenguajes descriptivos, XAML y QML, para el diseño de interfaces. Creo que debe haber un punto medio, tener encapsulación en lenguajes descriptivos no debería ser difícil, posiblemente sea un problema de HTML a solucionar, pero ese paso atrás no me gusta.

  2. @litox no estoy familiarizado con esos toolkits. De todas maneras te digo mis primeras impresiones (revisando por encima los primeros ejemplos que me encontré). QML no es un lenguaje de marcado, sino de programación. De hecho, por el primer vistazo que le he dado, diría que tiene muchas similitudes con React a nivel conceptual. XAML sí es un derivativo de XML y tendría que ver un ejemplo funcional para contestarte, no me ha quedado claro quién tiene el control de la composición con XAML. Fíjate que es una sutileza: que el control de la composición la tenga el lenguaje de programación no provoca que los lenguajes de marcado desaparezcan. Al contrario! Los necesitamos en el lugar donde son más adecuados y expresivos: definiendo “el reparto de píxeles” en la pantalla. 

  3. @nosolosw creo que lo que has visto de QML como lenguaje de programación no era QML era Javascript, que se puede usar dentro de QML pero creo que se usa de forma puntual, animaciones y ese tipo de cosas que necesitan de lenguajes imperativos, pero QML es puramente descriptivo, con una sintaxis parecida a CSS. XAML se utiliza mediante bindings con el lenguaje que quieras usar, muy parecido a HTML en ese caso. Creo que nadie pone en duda que el control se deba hacer mediante lenguaje de programación, cómo si no? De hecho no hay forma de hacer control desde HTML, como mínimo necesitas Javascript. No se, creo que algo se me escapa.

  4. @nosolosw lo he consultado con la almohada y ya lo veo más claro, tratas cada componente como una página independiente. Mi pregunta és, puedes tener un CSS general y luego otros específicos en esos frameworks? Porqué si no creo que pierde todo el sentido del uso del CSS.

  5. @litox sí, además del CSS general que va con la página -por decirlo de alguna manera- cada componente declara el suyo. Pero ten en cuenta que CSS quizás es la parte más hacky de todo esto. Al no poder tener contextos diferenciados, al final, las reglas CSS en el navegador van a ir todas en un mismo espacio global. Para evitar que las distintas reglas CSS entre componentes colisionen, una cosa que se suele hacer es que el nombre de la regla CSS incluya también el del componente. Así, por ejemplo, podrías tener las clases “.componente1bordechulo” y “.componente2bordechulo” para uso de cada componente y sus hijos. Como los nombres de los componentes sí son únicos, te aseguras que no colisionan. Por supuesto, sigues pudiendo tener una regla “.borde_chulo” que compartas a nivel de toda la aplicación.