Fluent Interface in Software Development

Lecture



Fluent interface (in the sense of a "smooth" or "flowing" interface) in software development is a way of implementing an object-oriented API aimed at improving the readability of program source code. The name was coined by Eric Evans and Martin Fowler.

method chaining

A fluent interface is good because it simplifies making multiple method calls on a single object. This is usually implemented by method chaining, where each method passes the call context on to the next link (but a fluent interface entails something more than just a chain of methods ). Usually, this context is:

  • defined by the value returned by a method;
  • inherited (the previous context is used as the new context);
  • terminated by returning a meaningless value (void).

This style is indirectly useful because it makes code more readable and intuitive . However, it can be very detrimental to debugging if the chain acts as a single expression, where the debugger cannot always set an intermediate breakpoint.

Implementation

A fluent interface is usually implemented using method chaining to achieve method cascading (in languages that do not natively support cascading), in particular because each method returns the object it is attached to, often called this or self. More abstractly, a fluent interface passes the context of the instruction on to the subsequent call in the method chain, where the context is usually

  • Defined through the return value of the called method
  • Self-referential, where the new context is equivalent to the last context
  • Terminated by returning an empty context

Note that a "fluent interface" means more than just method cascading through a chain; it entails designing an interface that reads like a DSL, using other techniques such as "nested functions and object scoping".

Examples

C#

Starting with C# 3.5 and above, advanced ways of implementing a fluent interface were introduced:

namespace Example.FluentInterfaces
{
    #region Standard Example
    public interface IConfiguration
    {
        string Color { set; }
        int Height { set; }
        int Length { set; }
        int Depth { set; }
    }
    public class Configuration : IConfiguration
    {
        string color;
        int height;
        int length;
        int depth;
        public string Color
        {
            set { color = value; }
        }
        public int Height
        {
            set { height = value; }
        }
        public int Length
        {
            set { length = value; }
        }
        public int Depth
        {
            set { depth = value; }
        }
    }
    #endregion
    #region Fluent Example
    public interface IConfigurationFluent
    {
        IConfigurationFluent SetColor(string color);
        IConfigurationFluent SetHeight(int height);
        IConfigurationFluent SetLength(int length);
        IConfigurationFluent SetDepth(int depth);
    }
    public class ConfigurationFluent : IConfigurationFluent
    {
        string color;
        int height;
        int length;
        int depth;
        public IConfigurationFluent SetColor(string color)
        {
            this.color = color;
            return this;
        }
        public IConfigurationFluent SetHeight(int height)
        {
            this.height = height;
            return this;
        }
        public IConfigurationFluent SetLength(int length)
        {
            this.length = length;
            return this;
        }
        public IConfigurationFluent SetDepth(int depth)
        {
            this.depth = depth;
            return this;
        }
    }
    #endregion
    public class ExampleProgram
    {
        public static void Main(string[] args)
        {
            // Regular example
            IConfiguration config = new Configuration
            {
                Color = "blue",
                Height = 1,
                Length = 2,
                Depth = 3
            };
            // Fluent interface example
            IConfigurationFluent fluentConfig =
                  new ConfigurationFluent().SetColor("blue")
                                           .SetHeight(1)
                                           .SetLength(2)
                                           .SetDepth(3);
        }
    }
}

C++

A trivial example in C++ is the standard iostream, where fluency is provided by operator overloading.

An example of a fluent interface wrapper in C++:

 // regular 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;
     }
     int 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();
 };
 // regular 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 interface wrapper
 class FluentGlutApp : private GlutApp {
 public:
     FluentGlutApp(int argc, char **argv) : GlutApp(argc, argv) {} // inherit the 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 does not matter whether the chain continues after create(), so we do not return *this
     void create() {
         GlutApp::create();
     }
 };
 // using the fluent interface
 int main(int argc, char **argv) {
     FluentGlutApp app(argc, argv)
         .withDoubleBuffer().withRGBA().withAlpha().withDepth()
         .at(200, 200).across(500, 500)
         .named("My OpenGL/GLUT App");
     app.create();
 }

Java

Some Java APIs implement this kind of interface, for example the 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();
}

The op4j library allows you to use a fluent interface for auxiliary tasks such as iterating over structures, converting data, filtering, and so on.

String[] datesStr = new String[] {"12-10-1492", "06-12-1978" };
...
List<Calendar> dates = 
    Op.on(datesStr).toList().map(FnString.toCalendar("dd-MM-yyyy")).get();

The EasyMock mock-object testing library also makes extensive use of this style to provide a convenient interface.

Collection mockCollection = EasyMock.createMock(Collection.class);
EasyMock.expect(mockCollection.remove(null)).andThrow(new NullPointerException()).atLeastOnce();

PHP

An example of a class with a fluent interface in PHP:

class Car {
	private $speed, $color, $doors;
		 
	public function setSpeed($speed){
		$this->speed = $speed;
		return $this;
	}
	 
	public function setColor($color)  {
		$this->color = $color;
		return $this;
	}
	 
	public function setDoors($doors) {
		$this->doors = $doors;
		return $this;
	}
}
	 	
// Regular implementation
$myCar2 = new Car();
$myCar2->setSpeed(100);
$myCar2->setColor('blue');
$myCar2->setDoors(5);
// Fluent interface
$myCar = new Car();
$myCar->setSpeed(100)->setColor('blue')->setDoors(5);

JavaScript

An example of a class with a fluent interface in JavaScript:

var Car = (function(){
	var speed, color, doors, pub;
		 
	function setSpeed(new_speed) {
		speed = new_speed;
		return pub;
	}
	 
	function setColor(new_color) {
		color = new_color;
		return pub;
	}
	 
	function setDoors(new_doors) {
		doors = new_doors;
		return pub;
	}
	pub = {
		'setSpeed': setSpeed,
		'setColor': setColor,
		'setDoors': setDoors,
	};
	return pub;
})
	
// Regular implementation
myCar2 = Car();
myCar2.setSpeed(100);
myCar2.setColor('blue');
myCar2.setDoors(5);
	 
// Fluent interface
myCar = Car();
myCar.setSpeed(100).setColor('blue').setDoors(5);

A different approach can also be used:

var $ = function(selector) {
    if(this.$) {
        return new $(selector);
    }
    if(typeof selector == "string") {
        this.init = document.getElementById(selector);
    }
};
 
$.prototype = {
    text: function(text) {
        if(!text){
           this.init.innerHTML;
        }
        this.init.innerHTML = text;
        return this;
    },
    css: function(style) {
        for(var i in style){
           this.init.style[i] = style[i];
        }
        return this;
    }
};
// usage example:
$('div').text('div').css({color: "red"});

An example of an implementation that does not depend on the type of the returned object:

({
    foo: function (a) {
        return a;
    }
}).foo('foo').toUpperCase();

See also

Design patterns
Fundamental
  • Delegation
  • Interface
  • Immutable
  • Functional design
Creational
  • Abstract factory
  • Object pool
  • Singleton
  • Lazy initialization
  • Prototype
  • Builder
  • Factory method
Structural
  • Adapter
  • Private class data
  • Decorator
  • Proxy
  • Composite
  • Bridge
  • Flyweight
  • Facade
Behavioral
  • Interpreter
  • Iterator
  • Command
  • Observer
  • Visitor
  • Mediator
  • State
  • Strategy
  • Memento
  • Null Object
  • Chain of responsibility
  • Template method
Concurrency
patterns
  • Double-checked locking
  • Single-threaded execution
  • Scheduler
  • Producer-consumer
  • Active object
Architectural
  • ActiveRecord
  • MVC (extension HMVC)
  • MVP
  • MVVM
  • PAC
  • Client-server
  • Service locator
Java EE patterns
  • J2EE_Patterns
Books
  • Design Patterns
People
  • Christopher Alexander
  • Erich Gamma
  • John Vlissides
  • Grady Booch
  • Kent Beck
  • Ward Cunningham
  • Martin Fowler
  • Robert Martin

Comments

To leave a comment

If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

Lectures and tutorial on "Web site or software design"

Terms: Web site or software design