Java Fluent API Design

In this article we will discuss about how to design fluent API in Java. The term Fluent interface is coined by Martin Fowler and Eric Evans. Fluent API means, build an API in such way so that same meets following criteria.
a.       API user can understand API very easily.
b.       API can perform a series of actions in order to finish a task, in java we can do it by series of method calls (Chaining of methods).
c.       Each Methods name should be Domain specific terminology.
d.       API should suggestive enough to guide API Users, what to do next and What are the possible operations users can take at a particular moment.
Suppose You want to Design an API for a Domain, say (Retail) so There should be some common terminology exists in Retail domain and for a certain context(Task) it will take a series of actions to finish this task. Say for an invoice generation it has to follow certain steps. Now when you design API, you should design it such a way, when API Users call Billing Service for invoice generation, API User can fluently perform each step in order to complete invoice generation and API will assist user to perform those steps upon invoking Billing Service. When a API method invokes by an user, method will perform its task and returns a Domain Object which will assist what to do next, until all steps are executed.  Unlike Standard API it is API user job to call API methods in a sequential way to successfully performs a task. So API Users has to know about the service steps very well.


Design A Fluent API:









   Example: Suppose we want to design a API for Restaurant.
As a customer of this Restaurant, one should follow below steps

In a Standard API design, we should do the following:
1.       Create a Restaurant interface.
2.        Create an Implementation class of Restaurant interface. Compose Menucard  class into it.
3.       Create getters and setters for restaurant properties like name, address.
4.       In MenuCard class maintain a List of menu Items . Expose some methods like showmenu(). Ordermenu(); etc.
5.       Each menu Items has name and cost properties and corresponding getters/setters.
6.      When API User call this API he/she will call a sequence of methods(Enter Resturent,call showMenu() then Ordermenu() etc.) to perform above Steps shown in the picture.

So it is not fluent lot of sequential statement needs to perform to complete task and API user has to know the sequence .

Now I will show you How we will design a Fluent API.
Java code:
package com.example.fluentapi.contract;

public interface IResturant {
      
       public IResturant name(String name);
       public IMenu show();
      
      

}


package com.example.fluentapi.contract;

public interface IMenu{
      
       public IMenu order(int index);
       public IMenu eat();
       public IMenu pay();
       public IItem get(int index);

}

package com.example.fluentapi.contract;

public interface IItem {
      
       public IItem name();
       public Integer cost();
      

}

Implementation:
package com.example.fluentapi.impl;

import com.example.fluentapi.contract.IMenu;
import com.example.fluentapi.contract.IResturant;

public class Arsalan implements IResturant{

                String name;
                String IMenu;
               
                public IResturant name(String name) {
                                this.name=name;
                                System.out.println("Enter to hotel :: " + name);
                                return this;
                }

               
                public IMenu show() {
                                // TODO Auto-generated method stub
                                ArsalanMenuHandler handler = new ArsalanMenuHandler();
                                handler.showMenu();
                                return handler;
                }

}

package com.example.fluentapi.impl;

import java.util.ArrayList;
import java.util.List;

import com.example.fluentapi.contract.IItem;
import com.example.fluentapi.contract.IMenu;

public class ArsalanMenuHandler implements IMenu{

                List<IItem> menuList = new ArrayList<IItem>();
                List<IItem> selectedList = new ArrayList<IItem>();
               
                public ArsalanMenuHandler()
                {
                                IItem biriyani = new IItem(){
                                                public IItem name()
                                                {
                                                                System.out.println("Mutton Biriyani");
                                                                return this;
                                                }
                                                public Integer cost()
                                                {
                                                                return 180;
                                                }
                                };
                                IItem muttonChap = new IItem(){
                                                public IItem name()
                                                {
                                                                System.out.println("Mutton Chap");
                                                                return this;
                                                }
                                                public Integer cost()
                                                {
                                                                return 160;
                                                }
                                };
                                IItem firni = new IItem(){
                                                public IItem name()
                                                {
                                                                System.out.println("Firni");
                                                                return this;
                                                }
                                                public Integer cost()
                                                {
                                                                return 100;
                                                }
                                               
                                               
                                };
                                menuList.add(biriyani);
                                menuList.add(muttonChap);
                                menuList.add(firni);
                               
                }
                public IMenu order(int index) {
                                // TODO Auto-generated method stub
                                IItem item =get(index);
                                selectedList.add(item);
                                System.out.println("Order given ::");
                                item.name();
                                return this;
                }

               
                public IMenu eat() {
                                for(IItem item : selectedList)
                                {
                                                System.out.println("eating ");
                                                item.name();
                                }
                                return this;
                }

               
                public IMenu pay() {
                                int cost=0;
                                for(IItem item : selectedList)
                                {
                                                cost = cost + item.cost();
                                }
                                System.out.println("Paying Ruppes" + cost);
                                return this;
                }
                @Override
                public IItem get(int index) {
                                // TODO Auto-generated method stub
                                if(index <3)
                                {
                                                return menuList.get(index);
                                }
                                return null;
                               
                }
               
                public void showMenu(){
                                System.out.println("MENU IN ARSALAN");
                                for(IItem item : menuList)
                                {
                                               
                                                item.name();
                                               
                                }
                               
                }

}

Test Fluent API:
package com.example.fluentapi.impl;

public class FluentApiTest {
      
       public static void main(String[] args) {
             
              new Arsalan().name("ARSALAN").show().order(0).order(1).eat().pay();
       }

}
Output :
Enter to hotel :: ARSALAN
MENU IN ARSALAN
Mutton Biriyani
Mutton Chap
Firni
Order given ::
Mutton Biriyani
Order given ::
Mutton Chap
eating
Mutton Biriyani
eating
Mutton Chap
Paying Ruppes340

Look How we perform the steps fluently by calling series of methods
new Arsalan().name("ARSALAN").show().order(0).order(1).eat().pay();

This is a test example, to make it more polished we need to work on following things
1.  Order method should take MenuItem instead of position also needs to handle Exception scenarios. If a MenuItem not found.
2.  Order can be cancelled.
3.  Pay must have payment mode. (creditcard, debitcard, Cash)
4.  May user can pay tips and it should be optional.
5.  In course of eating, he/she can Order more items, so make an order functionality should be available in course of eating.
6.  Tax calculation should be added in pay method.

Java 8  using Fluent API. 

stream api in java 8

JAVA8 Stream API
The most powerful feature in Java8 is introducing Java.Util.Stream package.

By Steam API, We can do filter,map,reduce like operations on a collection . It is very useful as many times we need to filter a collection based on some criteria.
Like, find the Employee who has a salary greater than 20000.

In order to do these prior to Java8, we need to iterate over the collection then check each employee’s salary. If it is greater than 20000 we need to put it in an another list. Then print out that list. This is very time-consuming and unnecessary work for a developer.

But in Java8 we can do it on the fly. No need to do all those prior java8 stuff.
Now we have Stream interface you can invoke stream() function or parallelStream function on Collection to get Stream Interface.

In-Stream interface you can do multiple useful operations like filter the stream based on
Criteria to do that just need to pass a Java.util.function.Predicate which contains the filter criteria.

As java8 also support Lambda Expression you can pass the predicate as an argument of a method.

Please read my article on java lambda Expression to know about this

Enough theory lets do some coding

Suppose I want to filter Employees  of a company based on their salary,department,sex,manager

By Java8 we do it very easily by using filter Operation

Step 1:
Create an Employee class

package com.example.java;

public class Employee {
           
            String name;
            String sex;
            boolean manager;
            Integer salary;
            String dept;
           
            public String getName() {
                        return name;
            }
            public void setName(String name) {
                        this.name = name;
            }
            public String getSex() {
                        return sex;
            }
            public void setSex(String sex) {
                        this.sex = sex;
            }
            public boolean isManager() {
                        return manager;
            }
            public void setManager(boolean manager) {
                        this.manager = manager;
            }
            public Integer getSalary() {
                        return salary;
            }
            public void setSalary(Integer salary) {
                        this.salary = salary;
            }
            public String getDept() {
                        return dept;
            }
            public void setDept(String dept) {
                        this.dept = dept;
            }
            @Override
            public String toString() {
                        return "Employee [name=" + name + ", sex=" + sex + ", manager="
                                                + manager + ", salary=" + salary + ", dept=" + dept + "]";
            }
           
           
           
           
           
           

}


Step 2:

Create another class which will perform filtering operation on Employee list

package com.example.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;

import com.example.java.Employee;

public class EmployeeUtiltyStream {
           

           
            List<Employee> list = new ArrayList<Employee>();
           
            public EmployeeUtiltyStream()
            {
                        init();
                       
            }
           
            private void init()
            {
                        Employee emp = new Employee();
                        emp.setName("Shamik Mitra");
                        emp.setManager(false);
                        emp.setSex("Male");
                        emp.setSalary(20000);
                        emp.setDept("Java");
                       
                        Employee emp1 = new Employee();
                        emp1.setName("Aman Verma");
                        emp1.setManager(true);
                        emp1.setSex("Male");
                        emp1.setSalary(50000);
                        emp1.setDept("IOT");
                       
                        Employee emp2 = new Employee();
                        emp2.setName("Priti Dey");
                        emp2.setManager(true);
                        emp2.setSex("FeMale");
                        emp2.setSalary(40000);
                        emp2.setDept("Mainframe");
                       
                        Employee emp3 = new Employee();
                        emp3.setName("Ajay Ghosh");
                        emp3.setManager(false);
                        emp3.setSex("Male");
                        emp3.setSalary(30000);
                        emp3.setDept("Java");
                       
                        list.add(emp);
                        list.add(emp1);
                        list.add(emp2);
                        list.add(emp3);
            }
           
            public void filterList(Predicate<Employee> predicate) {
                         
                        Stream<Employee> employeeStream = list.parallelStream();
                       
                        Stream<Employee> filteredEmployeeStream = employeeStream.filter(predicate);
                       
                        filteredEmployeeStream.forEach(employee->System.out.println(employee));
                       
            }
           
           
           
            public static void main(String[] args) {
                       
                        EmployeeUtiltyStream employeeUtiltyStream = new EmployeeUtiltyStream();
                       
                        employeeUtiltyStream.filterList(employee->employee.getSalary()<=40000);
                        System.out.println("****************************************");
                        employeeUtiltyStream.filterList( employee->employee.getSex().equalsIgnoreCase("FeMale"));
                        System.out.println("****************************************");
                        employeeUtiltyStream.filterList( employee->employee.isManager());
                        System.out.println("****************************************");
                        employeeUtiltyStream.filterList( employee->employee.getDept().equalsIgnoreCase("Java"));
                       
                       
                       
            }

}



Please look this class very carefully
I create a init method where I add Employees in a List.

Then I create a method call filterList which takes a java.util.function.Predicate class as argument

public void filterList(Predicate<Employee> predicate) {
                         
                        Stream<Employee> employeeStream = list.parallelStream();
                       
                        Stream<Employee> filteredEmployeeStream = employeeStream.filter(predicate);
                       
                        filteredEmployeeStream.forEach(employee->System.out.println(employee));
                       
            }


To get Stream class, I invoke parallelStram method on employee list.
Then call filter method and pass the predicate.

Now look at main method

employeeUtiltyStream.filterList(employee->employee.getSalary()<=40000);

here I invoke filterList method and pass Predicate using a lambda expression
in Predicate I say filter employee based on a salary less than 40,000

guess what output is

Employee [name=Shamik Mitra, sex=Male, manager=false, salary=20000, dept=Java]
Employee [name=Ajay Ghosh, sex=Male, manager=false, salary=30000, dept=Java]
Employee [name=Priti Dey, sex=FeMale, manager=true, salary=40000, dept=Mainframe]
****************************************
Employee [name=Priti Dey, sex=FeMale, manager=true, salary=40000, dept=Mainframe]
****************************************
Employee [name=Priti Dey, sex=FeMale, manager=true, salary=40000, dept=Mainframe]
Employee [name=Aman Verma, sex=Male, manager=true, salary=50000, dept=IOT]
****************************************
Employee [name=Ajay Ghosh, sex=Male, manager=false, salary=30000, dept=Java]

Employee [name=Shamik Mitra, sex=Male, manager=false, salary=20000, dept=Java]