Fluent interface
From Wikipedia, the free encyclopedia
In software engineering, a fluent interface (as first coined by Eric Evans and Martin Fowler) is a way of implementing an object oriented API in a way that provides for more readable code.
A fluent interface is normally implemented by using method chaining to relay the instruction context of a subsequent call (but a fluent interface entails more than just method chaining ). 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.
[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 depth; 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 depth; 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(); }
Some APIs in Java follows this pratice, like Java Persistence API:
public Collection<Student> findByNameAgeGender(String name, int age, Gender gender) { return em.createNamedQuery("Student.findByNameAgeGender") .setParameter("name", name) .setParameter("age", age) .setParameter("gender", gender) .setFirstResult(1) .setMaxResults(30) .setHint("hintName", "hintValue") .getResultList(); }
[edit] See also
[edit] External links
- Martin Fowler's original bliki entry coining the term
- A Delphi example of writing XML with a fluent interface
|