Fluent interfaces are a beautiful thing.
I mean, when you take something like this:
And compare it to this:
And doesn’t the second example feel more like the actual HTML fragment the code is creating? Even if you don’t have trouble reading the first example, having code to generate HTML that reads like what’s actually going on and nests like HTML elements helps you see what’s going on more clearly at a glance.
Remember, it’s not just about how terse you can make your code or what neat tricks you can do with it that matters; keeping in mind how your code clearly presents the end product is also important. And I would say
element.addId('id') more intuitively depicts what’s going on behind the scenes than
element.id = 'id'.
Plus it’s not really much fun to type all those variable declarations, equals signs, and semicolons.
…obviously that last bit is just my ever so humble (but accurate) opinion…
By the way, kids, always use semicolons — Andrea Giammarchi, paraphrased
No semicolons has side effects.
Fluent Interfaces, tl;dr
First, what is a fluent interface?
In software engineering, a fluent interface (as first coined by Eric Evans and Martin Fowler) is a method for designing object oriented APIs based extensively on method chaining with the goal of making the readability of the source code close to that of ordinary written prose, essentially creating a domain-specific language within the interface. (source)
Let’s say the list item has buttons to perform different actions for each task, like marking it complete. Assuming there’s some kind of data persistence on the backend, here’s what this could look like with jQuery:
The fluent interface makes the code much more compact and readable than it would have been otherwise.
How to create a simple fluent interface
We’ll build a small library to add and modify HTML elements to a document since working with the DOM is a great use case for this sort of thing.
Step 1: Define the constructor
I prefer using the old style constructor with methods declared on the prototype to ES2015’s
class syntax both because get off my lawn and, more importantly, because I agree with Eric Elliott that
How to Fix the ES6 `class` keyword
Make class inheritance compositional similar to the way stamps are composed. In other words, change the behavior of…
We’re just going to make a wrapper around a regular HTML element for simplicity. If the argument passed to the constructor is an instance of the DOM Level 2
HTMLElement interface we’ll just wrap it with the object. Otherwise it should be a string so we’ll use
document.createElement to make it.
We’ll also add a
create method to call the constructor and allow immediate chaining.
HtmlElement.create not only allows you to chain your element creation in the example above, but also absolves you of having to remember to use
new, which is a common source of bugs.
Step 2: Methods to add attributes and text content
Now let’s define the attribute methods used in the example. Note the use of the ES2015 rest parameter syntax in
addClasses. We’ll also set the element’s
textContent property and use shortcut evaluation with the
&& operators for cases where the relevant properties are
undefined. That will be relevant when we add methods to create child elements, in case we don’t want to specify all the properties for every child element.
Now you can create an element, id and any number of classes one nice method chain.
Obviously for a real application or library you’d need methods to add other attributes, including custom and data attributes. You’d also want methods to remove attributes as well, but this will do for an example.
Step 3: Add methods to nest child elements
These methods do exactly as advertised, adding one or more child elements to the parent.
Step 4: Methods to select and append elements
Obviously if we were using this in the wild we’d want more methods to select and manipulate elements, traverse the DOM, and more, but for demonstration purposes these three will do.
Step 5: Put it all together
Here’s the complete “class.”
Moving forward: What next?
Now that we’ve seen a basic implementation of a fluent interface, it’s simple to make additions and improvements to extend functionality.
Let me know in the responses if this is helpful, and how you might use fluent interfaces to improve your own projects in the future!