This is an old revision of this page, as edited by 72.43.214.108 (talk) at 14:48, 27 September 2007 (→Examples). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
Revision as of 14:48, 27 September 2007 by 72.43.214.108 (talk) (→Examples)(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# HelloWorld:
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(); }