Misplaced Pages

Fluent interface

Article snapshot taken from Wikipedia with creative commons attribution-sharealike license. Give it a read and then ask your questions in the chat. We can research this topic together.

This is an old revision of this page, as edited by 65.96.153.176 (talk) at 18:34, 3 September 2007 (added external link). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

Revision as of 18:34, 3 September 2007 by 65.96.153.176 (talk) (added external link)(diff) ← Previous revision | Latest revision (diff) | Newer revision → (diff)

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 offered by a fluent interface.

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 { 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.

// 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(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;
    }
    // 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();
}

External links