Fluent interface

From Wikipedia, the free encyclopedia

In software engineering, a fluent interface (as first coined by Eric Evans and Martin Fowler) is an objected oriented construct that defines a behavior capable of relaying the instruction context of a subsequent call. Generally, the context is

  • defined through the return value of a called method
  • self referential, where the new context is equivalent to the last context
  • terminated through the return of a void context.

This style is beneficial due to its ability to provide a more fluid feel to the code, although some engineers find the the style difficult to read. A second criticism is that generally, programming needs are too dynamic to rely on the static contact definition[vague] offered by a fluent interface.

[edit] Examples

The following example shows a class implementing a non-fluent interface, another class implementing a fluent counterpart of the aforementioned non-fluent interface, and differences in usage. The example is written in 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)
        {
            //Standard Example
            IConfiguration config = new Configuration();
            config.SetColor("blue");
            config.SetHeight(1);
            config.SetLength(2);
            config.SetDepth(3);
            //FluentExample
            IConfigurationFluent config = 
                  new ConfigurationFluent().SetColor("blue")
                                           .SetHeight(1)
                                           .SetLength(2)
                                           .SetDepth(3);
        }
    }
 }

The following is an example of providing a fluent interface wrapper on top of a more traditional interface in C++:

// basic definition
 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();
 };
 // basic usage
 int main(int argc, char **argv) {
     GlutApp app(argc, argv);
     app.setDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_ALPHA|GLUT_DEPTH); // Set framebuffer params
     app.setWindowSize(500, 500); // Set window params
     app.setWindowPosition(200, 200);
     app.setTitle("My OpenGL/GLUT App");
     app.create();
 }
 
 // Fluent wrapper
 class FluentGlutApp : private GlutApp {
 public:
     FluentGlutApp(int argc, char **argv) : GlutApp(argc, argv) {} // inherit parent constructor
     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(w, h);
         return *this;
     }
     FluentGlutApp &at(int x, int y) {
         setWindowPosition(x, y);
         return *this;
     }
     FluentGlutApp &named(const char *title) {
         setTitle(title);
         return *this;
     }
     // it doesn't make sense to chain after create(), so don't return *this
     void create() {
         GlutApp::create();
     }
 };
 // basic usage
 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();
 }

[edit] External links

Languages