Fluent interface

Last updated

In software engineering, a fluent interface is an object-oriented API whose design relies extensively on method chaining. Its goal is to increase code legibility by creating a domain-specific language (DSL). The term was coined in 2005 by Eric Evans and Martin Fowler. [1]

Contents

Implementation

A fluent interface is normally implemented by using method chaining to implement method cascading (in languages that do not natively support cascading), concretely by having each method return the object to which it is attached, often referred to as this or self. Stated more abstractly, a fluent interface relays the instruction context of a subsequent call in method chaining, where generally the context is

Note that a "fluent interface" means more than just method cascading via chaining; it entails designing an interface that reads like a DSL, using other techniques like "nested functions and object scoping". [1]

History

The term "fluent interface" was coined in late 2005, though this overall style of interface dates to the invention of method cascading in Smalltalk in the 1970s, and numerous examples in the 1980s. A common example is the iostream library in C++, which uses the << or >> operators for the message passing, sending multiple data to the same object and allowing "manipulators" for other method calls. Other early examples include the Garnet system (from 1988 in Lisp) and the Amulet system (from 1994 in C++) which used this style for object creation and property assignment.

Examples

C#

C# uses fluent programming extensively in LINQ to build queries using "standard query operators". The implementation is based on extension methods.

vartranslations=newDictionary<string,string>{{"cat","chat"},{"dog","chien"},{"fish","poisson"},{"bird","oiseau"}};// Find translations for English words containing the letter "a",// sorted by length and displayed in uppercaseIEnumerable<string>query=translations.Where(t=>t.Key.Contains("a")).OrderBy(t=>t.Value.Length).Select(t=>t.Value.ToUpper());// The same query constructed progressively:varfiltered=translations.Where(t=>t.Key.Contains("a"));varsorted=filtered.OrderBy(t=>t.Value.Length);varfinalQuery=sorted.Select(t=>t.Value.ToUpper());

Fluent interface can also be used to chain a set of method, which operates/shares the same object. Instead of creating a customer class, we can create a data context which can be decorated with fluent interface as follows.

// Defines the data contextclassContext{publicstringFirstName{get;set;}publicstringLastName{get;set;}publicstringSex{get;set;}publicstringAddress{get;set;}}classCustomer{privateContext_context=newContext();// Initializes the context// set the value for propertiespublicCustomerFirstName(stringfirstName){_context.FirstName=firstName;returnthis;}publicCustomerLastName(stringlastName){_context.LastName=lastName;returnthis;}publicCustomerSex(stringsex){_context.Sex=sex;returnthis;}publicCustomerAddress(stringaddress){_context.Address=address;returnthis;}// Prints the data to consolepublicvoidPrint(){Console.WriteLine($"First name: {_context.FirstName} \nLast name: {_context.LastName} \nSex: {_context.Sex} \nAddress: {_context.Address}");}}classProgram{staticvoidMain(string[]args){// Object creationCustomerc1=newCustomer();// Using the method chaining to assign & print data with a single linec1.FirstName("vinod").LastName("srivastav").Sex("male").Address("bangalore").Print();}}

C++

A common use of the fluent interface in C++ is the standard iostream, which chains overloaded operators.

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

// Basic definitionclassGlutApp{private:intw_,h_,x_,y_,argc_,display_mode_;char**argv_;char*title_;public:GlutApp(intargc,char**argv){argc_=argc;argv_=argv;}voidsetDisplayMode(intmode){display_mode_=mode;}intgetDisplayMode(){returndisplay_mode_;}voidsetWindowSize(intw,inth){w_=w;h_=h;}voidsetWindowPosition(intx,inty){x_=x;y_=y;}voidsetTitle(constchar*title){title_=title;}voidcreate(){;}};// Basic usageintmain(intargc,char**argv){GlutAppapp(argc,argv);app.setDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_ALPHA|GLUT_DEPTH);// Set framebuffer paramsapp.setWindowSize(500,500);// Set window paramsapp.setWindowPosition(200,200);app.setTitle("My OpenGL/GLUT App");app.create();}// Fluent wrapperclassFluentGlutApp:privateGlutApp{public:FluentGlutApp(intargc,char**argv):GlutApp(argc,argv){}// Inherit parent constructorFluentGlutApp&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(intw,inth){setWindowSize(w,h);return*this;}FluentGlutApp&at(intx,inty){setWindowPosition(x,y);return*this;}FluentGlutApp&named(constchar*title){setTitle(title);return*this;}// It doesn't make sense to chain after create(), so don't return *thisvoidcreate(){GlutApp::create();}};// Fluent usageintmain(intargc,char**argv){FluentGlutApp(argc,argv).withDoubleBuffer().withRGBA().withAlpha().withDepth().at(200,200).across(500,500).named("My OpenGL/GLUT App").create();}

Java

An example of a fluent test expectation in the jMock testing framework is: [1]

mock.expects(once()).method("m").with(or(stringContains("hello"),stringContains("howdy")));

The jOOQ library models SQL as a fluent API in Java:

Authorauthor=AUTHOR.as("author");create.selectFrom(author).where(exists(selectOne().from(BOOK).where(BOOK.STATUS.eq(BOOK_STATUS.SOLD_OUT)).and(BOOK.AUTHOR_ID.eq(author.ID))));

The fluflu annotation processor enables the creation of a fluent API using Java annotations.

The JaQue library enables Java 8 Lambdas to be represented as objects in the form of expression trees at runtime, making it possible to create type-safe fluent interfaces, i.e., instead of:

Customerobj=...obj.property("name").eq("John")

One can write:

method<Customer>(customer->customer.getName()=="John")

Also, the mock object testing library EasyMock makes extensive use of this style of interface to provide an expressive programming interface.

CollectionmockCollection=EasyMock.createMock(Collection.class);EasyMock.expect(mockCollection.remove(null)).andThrow(newNullPointerException()).atLeastOnce();

In the Java Swing API, the LayoutManager interface defines how Container objects can have controlled Component placement. One of the more powerful LayoutManager implementations is the GridBagLayout class which requires the use of the GridBagConstraints class to specify how layout control occurs. A typical example of the use of this class is something like the following.

GridBagLayoutgl=newGridBagLayout();JPanelp=newJPanel();p.setLayout(gl);JLabell=newJLabel("Name:");JTextFieldnm=newJTextField(10);GridBagConstraintsgc=newGridBagConstraints();gc.gridx=0;gc.gridy=0;gc.fill=GridBagConstraints.NONE;p.add(l,gc);gc.gridx=1;gc.fill=GridBagConstraints.HORIZONTAL;gc.weightx=1;p.add(nm,gc);

This creates a lot of code and makes it difficult to see what exactly is happening here. The Packer class provides a fluent mechanism, so you would instead write: [2]

JPanelp=newJPanel();Packerpk=newPacker(p);JLabell=newJLabel("Name:");JTextFieldnm=newJTextField(10);pk.pack(l).gridx(0).gridy(0);pk.pack(nm).gridx(1).gridy(0).fillx();

There are many places where fluent APIs can simplify how software is written and help create an API language that helps users be much more productive and comfortable with the API because the return value of a method always provides a context for further actions in that context.

JavaScript

There are many examples of JavaScript libraries that use some variant of this: jQuery probably being the most well known. Typically, fluent builders are used to implement "database queries", for example in the Dynamite client library:

// getting an item from a tableclient.getItem('user-table').setHashKey('userId','userA').setRangeKey('column','@').execute().then(function(data){// data.result: the resulting object})

A simple way to do this in JavaScript is using prototype inheritance and this.

// example from https://schier.co/blog/2013/11/14/method-chaining-in-javascript.htmlclassKitten{constructor(){this.name='Garfield';this.color='orange';}setName(name){this.name=name;returnthis;}setColor(color){this.color=color;returnthis;}save(){console.log(`saving ${this.name}, the ${this.color} kitten`);returnthis;}}// use itnewKitten().setName('Salem').setColor('black').save();

Scala

Scala supports a fluent syntax for both method calls and class mixins, using traits and the with keyword. For example:

classColor{defrgb():Tuple3[Decimal]}objectBlackextendsColor{overridedefrgb():Tuple3[Decimal]=("0","0","0");}traitGUIWindow{// Rendering methods that return this for fluent drawingdefset_pen_color(color:Color):this.typedefmove_to(pos:Position):this.typedefline_to(pos:Position,end_pos:Position):this.typedefrender():this.type=this// Don't draw anything, just return this, for child implementations to use fluentlydeftop_left():Positiondefbottom_left():Positiondeftop_right():Positiondefbottom_right():Position}traitWindowBorderextendsGUIWindow{defrender():GUIWindow={super.render().move_to(top_left()).set_pen_color(Black).line_to(top_right()).line_to(bottom_right()).line_to(bottom_left()).line_to(top_left())}}classSwingWindowextendsGUIWindow{...}valappWin=newSwingWindow()withWindowBorderappWin.render()

Raku

In Raku, there are many approaches, but one of the simplest is to declare attributes as read/write and use the given keyword. The type annotations are optional, but the native gradual typing makes it much safer to write directly to public attributes.

classEmployee {     subsetSalaryofRealwhere * > 0;     subsetNonEmptyStringofStrwhere * ~~ /\S/; # at least one non-space characterhasNonEmptyString$.nameisrw;     hasNonEmptyString$.surnameisrw;     hasSalary$.salaryisrw;      methodgist {         returnqq:to[END];        Name:    $.name        Surname: $.surname        Salary:  $.salary        END     } } my$employee = Employee.new();  given$employee {     .name    = 'Sally';     .surname = 'Ride';     .salary  = 200; }  say$employee;  # Output:# Name:    Sally# Surname: Ride# Salary:  200

PHP

In PHP, one can return the current object by using the $this special variable which represent the instance. Hence return $this; will make the method return the instance. The example below defines a class Employee and three methods to set its name, surname and salary. Each return the instance of the Employee class allowing to chain methods.

classEmployee{privatestring$name;privatestring$surname;privatestring$salary;publicfunctionsetName(string$name){$this->name=$name;return$this;}publicfunctionsetSurname(string$surname){$this->surname=$surname;return$this;}publicfunctionsetSalary(string$salary){$this->salary=$salary;return$this;}publicfunction__toString(){$employeeInfo='Name: '.$this->name.PHP_EOL;$employeeInfo.='Surname: '.$this->surname.PHP_EOL;$employeeInfo.='Salary: '.$this->salary.PHP_EOL;return$employeeInfo;}}# Create a new instance of the Employee class, Tom Smith, with a salary of 100:$employee=(newEmployee())->setName('Tom')->setSurname('Smith')->setSalary('100');# Display the value of the Employee instance:echo$employee;# Display:# Name: Tom# Surname: Smith# Salary: 100

Python

In Python, returning self in the instance method is one way to implement the fluent pattern.

It is however discouraged by the language’s creator, Guido van Rossum, [3] and therefore considered unpythonic (not idiomatic) for operations that do not return new values. Van Rossum provides string processing operations as example where he sees the fluent pattern appropriate.

classPoem:def__init__(self,title:str)->None:self.title=titledefindent(self,spaces:int):"""Indent the poem with the specified number of spaces."""self.title=" "*spaces+self.titlereturnselfdefsuffix(self,author:str):"""Suffix the poem with the author name."""self.title=f"{self.title} - {author}"returnself
>>> Poem("Road Not Travelled").indent(4).suffix("Robert Frost").title'    Road Not Travelled - Robert Frost'

Swift

In Swift 3.0+ returning self in the functions is one way to implement the fluent pattern.

classPerson{varfirstname:String=""varlastname:String=""varfavoriteQuote:String=""@discardableResultfuncset(firstname:String)->Self{self.firstname=firstnamereturnself}@discardableResultfuncset(lastname:String)->Self{self.lastname=lastnamereturnself}@discardableResultfuncset(favoriteQuote:String)->Self{self.favoriteQuote=favoriteQuotereturnself}}
letperson=Person().set(firstname:"John").set(lastname:"Doe").set(favoriteQuote:"I like turtles")

Immutability

It's possible to create immutable fluent interfaces that utilise copy-on-write semantics. In this variation of the pattern, instead of modifying internal properties and returning a reference to the same object, the object is instead cloned, with properties changed on the cloned object, and that object returned.

The benefit of this approach is that the interface can be used to create configurations of objects that can fork off from a particular point; Allowing two or more objects to share a certain amount of state, and be used further without interfering with each other.

JavaScript example

Using copy-on-write semantics, the JavaScript example from above becomes:

classKitten{constructor(){this.name='Garfield';this.color='orange';}setName(name){constcopy=newKitten();copy.color=this.color;copy.name=name;returncopy;}setColor(color){constcopy=newKitten();copy.name=this.name;copy.color=color;returncopy;}// ...}// use itconstkitten1=newKitten().setName('Salem');constkitten2=kitten1.setColor('black');console.log(kitten1,kitten2);// -> Kitten({ name: 'Salem', color: 'orange' }), Kitten({ name: 'Salem', color: 'black' })

Problems

Errors cannot be captured at compile time

In typed languages, using a constructor requiring all parameters will fail at compilation time while the fluent approach will only be able to generate runtime errors, missing all the type-safety checks of modern compilers. It also contradicts the "fail-fast" approach for error protection.

Debugging and error reporting

Single-line chained statements may be more difficult to debug as debuggers may not be able to set breakpoints within the chain. Stepping through a single-line statement in a debugger may also be less convenient.

java.nio.ByteBuffer.allocate(10).rewind().limit(100);

Another issue is that it may not be clear which of the method calls caused an exception, in particular if there are multiple calls to the same method. These issues can be overcome by breaking the statement into multiple lines which preserves readability while allowing the user to set breakpoints within the chain and to easily step through the code line by line:

java.nio.ByteBuffer.allocate(10).rewind().limit(100);

However, some debuggers always show the first line in the exception backtrace, although the exception has been thrown on any line.

Logging

Adding logging into the middle of a chain of fluent calls can be an issue. E.g., given:

ByteBufferbuffer=ByteBuffer.allocate(10).rewind().limit(100);

To log the state of buffer after the rewind() method call, it is necessary to break the fluent calls:

ByteBufferbuffer=ByteBuffer.allocate(10).rewind();log.debug("First byte after rewind is "+buffer.get(0));buffer.limit(100);

This can be worked around in languages that support extension methods by defining a new extension to wrap the desired logging functionality, for example in C# (using the same Java ByteBuffer example as above):

staticclassByteBufferExtensions{publicstaticByteBufferLog(thisByteBufferbuffer,Loglog,Action<ByteBuffer>getMessage){stringmessage=getMessage(buffer);log.debug(message);returnbuffer;}}// Usage:ByteBuffer.Allocate(10).Rewind().Log(log,b=>"First byte after rewind is "+b.Get(0)).Limit(100);

Subclasses

Subclasses in strongly typed languages (C++, Java, C#, etc.) often have to override all methods from their superclass that participate in a fluent interface in order to change their return type. For example:

classA{publicAdoThis(){...}}classBextendsA{publicBdoThis(){super.doThis();returnthis;}// Must change return type to B.publicBdoThat(){...}}...Aa=newB().doThat().doThis();// This would work even without overriding A.doThis().Bb=newB().doThis().doThat();// This would fail if A.doThis() wasn't overridden.

Languages that are capable of expressing F-bound polymorphism can use it to avoid this difficulty. For example:

abstractclassAbstractA<TextendsAbstractA<T>>{@SuppressWarnings("unchecked")publicTdoThis(){...;return(T)this;}}classAextendsAbstractA<A>{}classBextendsAbstractA<B>{publicBdoThat(){...;returnthis;}}...Bb=newB().doThis().doThat();// Works!Aa=newA().doThis();// Also works.

Note that in order to be able to create instances of the parent class, we had to split it into two classes — AbstractA and A, the latter with no content (it would only contain constructors if those were needed). The approach can easily be extended if we want to have sub-subclasses (etc.) too:

abstractclassAbstractB<TextendsAbstractB<T>>extendsAbstractA<T>{@SuppressWarnings("unchecked")publicTdoThat(){...;return(T)this;}}classBextendsAbstractB<B>{}abstractclassAbstractC<TextendsAbstractC<T>>extendsAbstractB<T>{@SuppressWarnings("unchecked")publicTfoo(){...;return(T)this;}}classCextendsAbstractC<C>{}...Cc=newC().doThis().doThat().foo();// Works!Bb=newB().doThis().doThat();// Still works.

In a dependently typed language, e.g. Scala, methods can also be explicitly defined as always returning this and thus can be defined only once for subclasses to take advantage of the fluent interface:

classA{defdoThis():this.type={...}// returns this, and always this.}classBextendsA{// No override needed!defdoThat():this.type={...}}...vala:A=newB().doThat().doThis();// Chaining works in both directions.valb:B=newB().doThis().doThat();// And, both method chains result in a B!

See also

Related Research Articles

OCaml is a general-purpose, high-level, multi-paradigm programming language which extends the Caml dialect of ML with object-oriented features. OCaml was created in 1996 by Xavier Leroy, Jérôme Vouillon, Damien Doligez, Didier Rémy, Ascánder Suárez, and others.

The bridge pattern is a design pattern used in software engineering that is meant to "decouple an abstraction from its implementation so that the two can vary independently", introduced by the Gang of Four. The bridge uses encapsulation, aggregation, and can use inheritance to separate responsibilities into different classes.

In computer programming, lazy initialization is the tactic of delaying the creation of an object, the calculation of a value, or some other expensive process until the first time it is needed. It is a kind of lazy evaluation that refers specifically to the instantiation of objects or other resources.

In object oriented programming, the factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify their exact class. Rather than by calling a constructor, this is done by by calling a factory method to create an object. Factory methods can either be specified in an interface and implemented by child classes, or implemented in a base class and optionally overridden by derived classes.

In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, dynamically, without affecting the behavior of other instances of the same class. The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern as well as to the Open-Closed Principle, by allowing the functionality of a class to be extended without being modified. Decorator use can be more efficient than subclassing, because an object's behavior can be augmented without defining an entirely new object.

In object-oriented (OO) and functional programming, an immutable object is an object whose state cannot be modified after it is created. This is in contrast to a mutable object, which can be modified after it is created. In some cases, an object is considered immutable even if some internally used attributes change, but the object's state appears unchanging from an external point of view. For example, an object that uses memoization to cache the results of expensive computations could still be considered an immutable object.

In cryptography, the shrinking generator is a form of pseudorandom number generator intended to be used in a stream cipher. It was published in Crypto 1993 by Don Coppersmith, Hugo Krawczyk and Yishay Mansour.

<span class="mw-page-title-main">C syntax</span> Set of rules defining correctly structured programs

The syntax of the C programming language is the set of rules governing writing of software in C. It is designed to allow for programs that are extremely terse, have a close relationship with the resulting object code, and yet provide relatively high-level data abstraction. C was the first widely successful high-level language for portable operating-system development.

<span class="mw-page-title-main">Pointer (computer programming)</span> Object which stores memory addresses in a computer program

In computer science, a pointer is an object in many programming languages that stores a memory address. This can be that of another value located in computer memory, or in some cases, that of memory-mapped computer hardware. A pointer references a location in memory, and obtaining the value stored at that location is known as dereferencing the pointer. As an analogy, a page number in a book's index could be considered a pointer to the corresponding page; dereferencing such a pointer would be done by flipping to the page with the given page number and reading the text found on that page. The actual format and content of a pointer variable is dependent on the underlying computer architecture.

In compiler construction, name mangling is a technique used to solve various problems caused by the need to resolve unique names for programming entities in many modern programming languages.

<span class="mw-page-title-main">Method overriding</span> Language feature in object-oriented programming

Method overriding, in object-oriented programming, is a language feature that allows a subclass or child class to provide a specific implementation of a method that is already provided by one of its superclasses or parent classes. In addition to providing data-driven algorithm-determined parameters across virtual network interfaces, it also allows for a specific type of polymorphism (subtyping). The implementation in the subclass overrides (replaces) the implementation in the superclass by providing a method that has same name, same parameters or signature, and same return type as the method in the parent class. The version of a method that is executed will be determined by the object that is used to invoke it. If an object of a parent class is used to invoke the method, then the version in the parent class will be executed, but if an object of the subclass is used to invoke the method, then the version in the child class will be executed. This helps in preventing problems associated with differential relay analytics which would otherwise rely on a framework in which method overriding might be obviated. Some languages allow a programmer to prevent a method from being overridden.

<span class="mw-page-title-main">Fox toolkit</span>

The FOX toolkit is an open-source, cross-platform widget toolkit, i.e. a library of basic elements for building a graphical user interface (GUI). FOX stands for Free Objects for X.

<span class="mw-page-title-main">Java syntax</span> Set of rules defining correctly structured program

The syntax of Java is the set of rules defining how a Java program is written and interpreted.

In computer programming, an entry point is the place in a program where the execution of a program begins, and where the program has access to command line arguments.

sizeof is a unary operator in the programming languages C and C++. It generates the storage size of an expression or a data type, measured in the number of char-sized units. Consequently, the construct sizeof (char) is guaranteed to be 1. The actual number of bits of type char is specified by the preprocessor macro CHAR_BIT, defined in the standard include file limits.h. On most modern computing platforms this is eight bits. The result of sizeof has an unsigned integer type that is usually denoted by size_t.

<span class="mw-page-title-main">RDFLib</span> Python library to serialize, parse and process RDF data

RDFLib is a Python library for working with RDF, a simple yet powerful language for representing information. This library contains parsers/serializers for almost all of the known RDF serializations, such as RDF/XML, Turtle, N-Triples, & JSON-LD, many of which are now supported in their updated form. The library also contains both in-memory and persistent Graph back-ends for storing RDF information and numerous convenience functions for declaring graph namespaces, lodging SPARQL queries and so on. It is in continuous development with the most recent stable release, rdflib 6.1.1 having been released on 20 December 2021. It was originally created by Daniel Krech with the first release in November, 2002.

This article describes the syntax of the C# programming language. The features described are compatible with .NET Framework and Mono.

In computer science, marshalling or marshaling is the process of transforming the memory representation of an object into a data format suitable for storage or transmission, especially between different runtimes. It is typically used when data must be moved between different parts of a computer program or from one program to another.

A code sanitizer is a programming tool that detects bugs in the form of undefined or suspicious behavior by a compiler inserting instrumentation code at runtime. The class of tools was first introduced by Google's AddressSanitizer of 2012, which uses directly mapped shadow memory to detect memory corruption such as buffer overflows or accesses to a dangling pointer (use-after-free).

re2c is a free and open-source lexer generator for C, C++, Go, and Rust. It compiles declarative regular expression specifications to deterministic finite automata. Originally written by Peter Bumbulis and described in his paper, re2c was put in public domain and has been since maintained by volunteers. It is the lexer generator adopted by projects such as PHP, SpamAssassin, Ninja build system and others. Together with the Lemon parser generator, re2c is used in BRL-CAD. This combination is also used with STEPcode, an implementation of ISO 10303 standard.

References

  1. 1 2 3 Martin Fowler, "FluentInterface", 20 December 2005
  2. "Interface Pack200.Packer". Oracle. Retrieved 13 November 2019.
  3. Rossum, Guido van (October 17, 2003). "[Python-Dev] sort() return value" . Retrieved 2022-02-01.