miércoles, 14 de noviembre de 2007
Pues no, esta entrada no trata de los diseños líquidos habituales en el mundo de la maquetación web, aunque podría parecer lo contrario. El término interfaz se utiliza en su acepción relativa a la orientación a objetos, y la fluidez se refiere a la continuidad en el movimiento de instancias entre distintas llamadas a métodos.
En pocas palabras, el uso de interfaces fluidos es un estilo cada vez más frecuente de programación, también llamado a veces "encadenamiento de métodos" (method chaining), que promueve la eliminación de código innecesario y engorroso para la realización de tareas frecuentes, sustituyéndolo por una secuencia natural, intuitiva y fluida de instrucciones que se encadenan de forma casi mágica.
Pero veámoslo con un ejemplo. Y ojo, que aunque lo codificaremos en C#, el concepto es perfectamente válido para otros lenguajes orientados a objetos, salvo por los aspectos sintácticos.
Imaginemos la clase
La forma habitual de operar con esta calculadora para obtener el resultado de un cálculo sería algo parecido a:
Como podemos observar, para realizar unas operaciones muy simples hemos necesitado demasiadas líneas de código, y además bastante repetitivas.
Veamos ahora cómo utilizando la técnica de interfaces fluidos podemos dejar este código en:
El truco consiste en hacer que métodos que normalmente no devolverían ningún valor (métodos void, como los de la clase Calculator anterior) retornen referencias hacia el propio objeto, es decir, acaben con un
Otro ejemplo, donde se utiliza la misma técnica, pero esta vez combinando instancias de distintas clases (fijaos que nada impide en la mayoría de lenguajes dividir las líneas de código como se muestra, facilitando así la lectura):
Este código muestra tres aspectos importantes. Primero, que es posible crear un pseudo-lenguaje de programación utilizando esta técnica, de hecho los fluent interfaces son muy utilizados en los DSL (Lenguajes Específicos de Dominio). En el ejemplo se puede intuir un lenguaje para el dibujo de figuras sobre una superficie.
Segundo, que la devolución de los métodos no tiene por qué limitarse a una única clase. En el código anterior, los métodos
Tercero, se deja entrever la dificultad de desarrollar clases que permitan utilizar esta técnica, pues cada método debe devolver una referencia al objeto oportuno para facilitar el encadenamiento con el método posterior. Una elección incorrecta de la clase de devolución hará que la fluidez se rompa.
Finalmente, decir que los interfaces fluidos no son algo nuevo. Hay quien habla de su uso en SmallTalk antes de la década de los noventa (!), aunque el concepto se extendió a partir de la inclusión de una entrada en la Bliki de Martin Fowler comentándolo hace un par de años.
Hay también quien opina que los interfaces fluidos son algo más que el encadenamiento de métodos, que también existe en un código como
En cualquier caso, hace algún tiempo que esta técnica me llamó la atención y cada vez lo veo utilizado en más ocasiones, especialmente en contextos de frameworks y para implementar DSLs (como Quaere).
En pocas palabras, el uso de interfaces fluidos es un estilo cada vez más frecuente de programación, también llamado a veces "encadenamiento de métodos" (method chaining), que promueve la eliminación de código innecesario y engorroso para la realización de tareas frecuentes, sustituyéndolo por una secuencia natural, intuitiva y fluida de instrucciones que se encadenan de forma casi mágica.
Pero veámoslo con un ejemplo. Y ojo, que aunque lo codificaremos en C#, el concepto es perfectamente válido para otros lenguajes orientados a objetos, salvo por los aspectos sintácticos.
Imaginemos la clase
Calculator
, con un diseño como el mostrado a la izquierda, que es capaz de mantener un único valor (entero) y que soporta cinco operaciones: Get
(para obtener el valor actual de la calculadora), Set
(para establecerlo), y Add
, Substract
y Multiply
(para sumarle, restarle y multiplicarle un valor respectivamente).La forma habitual de operar con esta calculadora para obtener el resultado de un cálculo sería algo parecido a:
Calculator calc = new Calculator();
calc.Set(0);
calc.Add(10);
calc.Multiply(2);
calc.Subtract(4);
Console.WriteLine("Resultado : " + calc.Get());
Como podemos observar, para realizar unas operaciones muy simples hemos necesitado demasiadas líneas de código, y además bastante repetitivas.
Veamos ahora cómo utilizando la técnica de interfaces fluidos podemos dejar este código en:
Console.WriteLine("Resultado: " +
new Calculator().Set(0).Add(10).Multiply(2)
.Substract(4).Get()
);
El truco consiste en hacer que métodos que normalmente no devolverían ningún valor (métodos void, como los de la clase Calculator anterior) retornen referencias hacia el propio objeto, es decir, acaben con un
return this;
. De esta forma, la llamada a la función podrá ir seguida de otra llamada sobre el mismo objeto, que a su vez lo devolverá para el siguiente y así sucesivamente. Y de ahí el término "fluido": fijaos en el ejemplo anterior como la instancia de Calculator que es retornada por el constructor se utiliza directamente en la llamada Set(0)
, que a su vez la devuelve y es utilizada por Add(10)
... ¿no es eso fluir, aunque el término suene algo poético?Otro ejemplo, donde se utiliza la misma técnica, pero esta vez combinando instancias de distintas clases (fijaos que nada impide en la mayoría de lenguajes dividir las líneas de código como se muestra, facilitando así la lectura):
new Surface()
.Clear()
.Fill("white")
.CreateText()
.SetMessage("Hi, all!")
.SetColor("blue")
.Draw()
.CreateCircle(100, 100, 10)
.Fill("green")
.Draw()
.Border("red")
.Draw()
Este código muestra tres aspectos importantes. Primero, que es posible crear un pseudo-lenguaje de programación utilizando esta técnica, de hecho los fluent interfaces son muy utilizados en los DSL (Lenguajes Específicos de Dominio). En el ejemplo se puede intuir un lenguaje para el dibujo de figuras sobre una superficie.
Segundo, que la devolución de los métodos no tiene por qué limitarse a una única clase. En el código anterior, los métodos
CreateText()
y CreateCircle()
retornan referencias a nuevas instancias de clases que representan estas figuras, que también hacen uso de interfaces fluidos. Los métodos Draw()
de ambas retornan de nuevo una referencia a la instancia de la superficie (Surface
) sobre la que están dibujando.Tercero, se deja entrever la dificultad de desarrollar clases que permitan utilizar esta técnica, pues cada método debe devolver una referencia al objeto oportuno para facilitar el encadenamiento con el método posterior. Una elección incorrecta de la clase de devolución hará que la fluidez se rompa.
Finalmente, decir que los interfaces fluidos no son algo nuevo. Hay quien habla de su uso en SmallTalk antes de la década de los noventa (!), aunque el concepto se extendió a partir de la inclusión de una entrada en la Bliki de Martin Fowler comentándolo hace un par de años.
Hay también quien opina que los interfaces fluidos son algo más que el encadenamiento de métodos, que también existe en un código como
name.Trim().ToUpper()
, pues aporta una interfaz comprensible e intuitiva.En cualquier caso, hace algún tiempo que esta técnica me llamó la atención y cada vez lo veo utilizado en más ocasiones, especialmente en contextos de frameworks y para implementar DSLs (como Quaere).
Publicado por José M. Aguilar a las 8:48 p. m.
Etiquetas: desarrollo, dsl, fluent interfaces, interfaces fluidos, patrones, programación
4 Comentarios:
José, muy interesante técnica, creo que sin duda aporta legibilidad de código.
Se me ocurre que se puede diseñar facilmente para trabajar de ambas maneras sobrecargando los métodos, algo así como:
void Add(int operando){...}
Calculator Add(int operando)
{
Add(operando);
return this;
}
Saludos
Hola, Alejandro, ante todo gracias por comentar.
Correcto, eso sería una forma de añadir fluidez a clases existentes. Obviamente, las que se desarrollen directamente usando fluent interfaces pueden usarse de forma normal, simplemente ignorando el retorno.
Hola hombre, esto de las interfaces esta excelente. Muchas gracias.
Solo para que el error no quede NO se puede hacer algo como esto que comenta nuestro colega alejando
void Add(int operando){...}
Calculator Add(int operando)
La sobrecarga de metodos depende de los parametros y no del tipo a retornar.
Perfecto el articulo
Enviar un nuevo comentario