En ingeniería de software, una interfaz fluida (término acuñado por primera vez por Eric Evans y Martin Fowler) es una construcción orientada a objeto que define un comportamiento capaz de retransmitir el contexto de la instrucción de una llamada subsecuente. Generalmente, el contexto es

  • definido a través del valor de retorno de un método llamado
  • autoreferencial, donde el nuevo contexto es equivalente al contexto anterior
  • terminado por medio del retorno de un contexto vacío (void context).

Este estilo es beneficioso debido a su capacidad de proporcionar una sensación más fluida al código, aunque algunos ingenieros encuentran el estilo difícil de leer. Una segunda crítica es que generalmente, las necesidades de programación son demasiado dinámicas para confiar en la definición estática de contacto ofrecida por una interfaz fluida.

Ejemplos editar

El siguiente ejemplo muestra una clase implementando una interfaz no fluida, y otra implementando una contraparte fluida, junto con las diferencias en el uso. El ejemplo se escribe en C #:

 namespace Example.FluentInterfaces
 {
    using System;
    public interface IConfiguration
    {
        void SetColor(string color);
        void SetHeight(int height);
        void SetLength(int length);
        void SetDepth(int depth);
    }

    public interface IConfigurationFluent
    {
        IConfigurationFluent SetColor(string color);
        IConfigurationFluent SetHeight(int height);
        IConfigurationFluent SetLength(int length);
        IConfigurationFluent SetDepth(int depth);
    }

    public class Configuration : IConfiguration
    {
        string color;
        int height;
        int length;
        int width;
        void SetColor(string color)
        {
           this.color = color;
        }
        void SetHeight(int height)
        {
           this.height = height;
        }
        void SetLength(int length)
        {
           this.length = length;
        }
        void SetDepth(int depth)
        {
           this.depth = depth;
        }
    }

    public class ConfigurationFluent : IConfigurationFluent
    {
        string color;
        int height;
        int length;
        int width;
        IConfigurationFluent SetColor(string color)
        {
           this.color = color;
           return this;
        }
        IConfigurationFluent SetHeight(int height)
        {
           this.height = height;
           return this;
        }
        IConfigurationFluent SetLength(int length)
        {
           this.length = length;
           return this;
        }
        IConfigurationFluent SetDepth(int depth)
        {
           this.depth = depth;
           return this;
        }
    }

    public class ExampleProgram
    {
        [STAThread]
        public static void Main(string[] args)
        {
            //Ejemplo estándar
            IConfiguration config = new Configuration();
            config.SetColor("blue");
            config.SetHeight(1);
            config.SetLength(2);
            config.SetDepth(3);
            //Ejemplo fluido
            IConfigurationFluent config = 
                  new ConfigurationFluent().SetColor("blue")
                                           .SetHeight(1)
                                           .SetLength(2)
                                           .SetDepth(3);
        }
    }
 }

El siguiente es un ejemplo en C++ de cómo proveer una envoltura de una interfaz fluida arriba de una interfaz más tradicional:

 // definición básica
 class GlutApp {
 private:
     int w_, h_, x_, y_, argc_, display_mode_;
     char **argv_;
     char *title_;
 public:
     GlutApp(int argc, char** argv) {
         argc_ = argc;
         argv_ = argv;
     }
     void setDisplayMode(int mode) {
         display_mode_ = mode;
     }
     void getDisplayMode() {
         return display_mode_;
     }
     void setWindowSize(int w, int h) {
         w_ = w;
         h_ = h;
     }
     void setWindowPosition(int x, int y) {
         x_ = x;
         y_ = y;
     }
     void setTitle(const char *title) {
         title_ = title;
     }
     void create();
 };
 // uso básico
 int main(int argc, char **argv) {
     GlutApp app(argc, argv);
     app.setDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_ALPHA|GLUT_DEPTH); // Ajusta los parámetros del framebuffer
     app.setWindowSize(500, 500); // Ajusta los parámetros de la ventana
     app.setWindowPosition(200, 200);
     app.setTitle("My OpenGL/GLUT App");
     app.create();
 }

 // Envoltorio fluido
 class FluentGlutApp : private GlutApp {
 public:
     FluentGlutApp(int argc, char **argv) : GlutApp(argc, argv) {} // hereda el constructor del pariente
     FluentGlutApp &withDoubleBuffer() {
         setDisplayMode(getDisplayMode() | GLUT_DOUBLE);
         return *this;
     }
     FluentGlutApp &withRGBA() {
         setDisplayMode(getDisplayMode() | GLUT_RGBA);
         return *this;
     }
     FluentGlutApp &withAlpha() {
         setDisplayMode(getDisplayMode() | GLUT_ALPHA);
         return *this;
     }
     FluentGlutApp &withDepth() {
         setDisplayMode(getDisplayMode() | GLUT_DEPTH);
         return *this;
     }
     FluentGlutApp &across(int w, int h) {
         setWindowSize(int w, int h) {
         return *this;
     }
     FluentGlutApp &at(int x, int y) {
         setWindowPosition(x, y);
         return *this;
     }
     FluentGlutApp &named(const char *title) {
         setTitle(title);
         return *this;
     }
     // no tiene sentido encadenar después de create(), así que se retorna *this
     void create() {
         GlutApp::create();
     }
 };
 // uso básico
 int main(int argc, char **argv) {
     FluentGlutApp app(argc, argv)
         .withDouble().withRGBA().withAlpha().withDepth()
         .at(200, 200).across(500, 500)
         .named("My OpenGL/GLUT App");
     app.create();
 }

Enlaces externos editar