Decoradores, patrones de diseño multilenguaje.


Mucho han evolucionado los lenguajes de programación desde que Ada Lovelace codificara los primeros bucles para la máquina de Charles Babbage. Los diferentes lenguajes intentan ser cada vez más simples y automáticos para facilitar la codificación de complejas relaciones con pocas líneas de código. Macros, templates o patrones de diseño, son herramientas que se incorporan cada vez más a la codificación, limpiando el código y estableciendo relaciones de compilación. Uno de esos patrones de diseño son los decoradores.

Este patrón de diseño, definido en varios lenguajes de programación, es muy recomendable cuando queremos añadir una misma funcionalidad a un conjunto de funciones de una o varias clases. Creando una función nueva que encapsulará a las funciones ya definidas de forma que se ejecutaran las nuevas instrucciones antes o después de llamar a la función original. Según se desee.

Por lo que solo será necesario añadir esta encapsulación o decoración a la declaración de las funciones originales. Decorándolas. Y por eso este patrón ha adoptado este nombre. Decoradores. Aunque también pueden llamarse wrappers.

La implementación de los decoradores depende de cada lenguaje. Normalmente se definen mediante dos capas de llamadas, o tres si se quieren utilizar parámetros en el decorador. El lenguaje de programación más sencillo para implementar este patrón es Python. También suelen utilizarse en JAVA, C# o PHP. E incluso pueden implementarse en C++, aunque en este último es menos común.

La forma de decorar también depende del lenguaje. En Python las funciones se decoran en la línea superior con un @ seguido del nombre del decorador, mientras que en otros lenguajes se implementa como encapsulamiento de clases heredadas.

Sea cual sea el lenguaje en el que implementemos nuestros proyectos, los decoradores son un buen comodín a la hora de simplificar y optimizar el código. Además de que ayudan a abordar la escalabilidad de los proyectos ya que podemos añadir varias funcionalidades al código ya implementado añadiendo varios decoradores a cada función.

Vamos con algunos ejemplos:

Python

Este lenguaje de programación no tipado ni compilado, amado y odiado a partes iguales por los programadores, es posiblemente el ejemplo más sencillo para entender el encapsulamiento y flujo de un decorador.

Hay que señalar que las funciones en Python se definen mediante la palabra reservada «def«. De forma todo lo que quede detrás de los «:» se considera el cuerpo de la función hasta que se recupere la tabulación de las líneas. Además, una función puede contener la definición de otras funciones en su interior encadenando varios «def xxx:» seguidos.

Otra característica importante de Python es que las funciones se pueden tratar como objetos si se les quitan los «()». Pudiéndose pasar por parámetro para definir otras funciones a modo de callbacks entre clases o encadenando las llamadas como en este caso. Podría decirse por tanto que el decorador fragmenta la definición original de forma que la función pasa a ser un objeto de la función de decoración.

El ejemplo más común para mostrar la funcionalidad de un decorador es una función que imprima un resultado. En dicho ejemplo se desea llamar a la función original e imprimir el retorno de esta. En este caso definimos el decorador en dos niveles de llamadas quedando:

decorador en python
Ejemplo de decorador implementado en Python

Mientras que, si queremos añadir un parámetro en el decorador, deberíamos añadirlo en otro nivel de funciones.

decoradores en python con parámetros
Ejemplo de decorador con parámetros implementado en Python

Algunas librerías públicas de python incluyen funcionalidades que podemos incluir en nuestro código definidas como decoradores ya consolidados. También existe un estandar nemónico para la declaración de decoradores. Algunos ejemplos son:

  • @staticxxxx se usa para métodos estáticos
  • @classxxxx para indicar que es propio de una clase
  • @property para definir propiedades de una clase como nombre o índice

Otros lenguajes

En el resto de lenguajes, el encapsulamiento o wrapper se realiza mediante herencia de funciones, de forma que se crea una interfaz entre la llamada a la función y la propia función que modifica y amplia el resultado. Suelen definirse como clases abstractas o como extends según el lenguaje. Pero en todos los casos se suele utilizar el nombre decorator dentro del nombre de la clase para caracterizar el patrón.

En la página de Refactoring Guru podemos ver ejemplos completos en los diferentes lenguajes.





Para leer más:

Deja un comentario