The evolution of Lambda
Hi Guys,
Let us look at the behavioral parameter pattern, the base of lambda function and the evolution of lambda function. Change is inevitable. Requirements do change. It is the ability of the software to respond well that makes it stand out in the crowd. Behavioral parameter pattern lets us handle frequent changes in the most beautiful way. Let us discuss with an example.
Requirement: We have a list of employees. We want to filter only full time employees. How can we write java code for this.
Solution 1:
public List<Employee> filterFulltimeEmployee(List<Employee> employees)
{ List<Employee> filteredEmployees = new ArrayList<>();
for(Employee employee : employees) {
if(employee.isFulltimeEmployee) filteredEmployees.add(employee);
}
return filteredEmployees;
}
{ List<Employee> filteredEmployees = new ArrayList<>();
for(Employee employee : employees) {
if(employee.isFulltimeEmployee) filteredEmployees.add(employee);
}
return filteredEmployees;
}
Fine. Now instead of full time employees, we need part time employees. A naive solution would be to implement another method like below.
public List<Employee> filterParttimeEmployee(List<Employee> employees)
{
List<Employee> filteredEmployees = new ArrayList<>();
for(Employee employee : employees)
{
if(!employee.isFulltimeEmployee)
filteredEmployees.add(employee);
}
return filteredEmployees;
}
{
List<Employee> filteredEmployees = new ArrayList<>();
for(Employee employee : employees)
{
if(!employee.isFulltimeEmployee)
filteredEmployees.add(employee);
}
return filteredEmployees;
}
But isn't most of the lines duplicated. Except the condition all the other lines of codes duplicate. Another approach is to pass a parameter specifying whether you need fulltime or parttime employee.
Solution 2:
public List<Employee> filterEmployee(List<Employee> employees, boolean needFulltimeEmployee)
{
List<Employee> filteredEmployees = new ArrayList<>();
for (Employee employee : employees)
{
if (needFulltimeEmployee ^ !employee.isFulltimeEmployee)
filteredEmployees.add(employee);
}
return filteredEmployees;
}
{
List<Employee> filteredEmployees = new ArrayList<>();
for (Employee employee : employees)
{
if (needFulltimeEmployee ^ !employee.isFulltimeEmployee)
filteredEmployees.add(employee);
}
return filteredEmployees;
}
Sounds good. Right? Now let us filter only managers.
public List<Employee> filterManager(List<Employee> employees)
{
List<Employee> filteredEmployees = new ArrayList<>();
for(Employee employee : employees)
{
if(employee.isManager)
filteredEmployees.add(employee);
}
return filteredEmployees;
}
{
List<Employee> filteredEmployees = new ArrayList<>();
for(Employee employee : employees)
{
if(employee.isManager)
filteredEmployees.add(employee);
}
return filteredEmployees;
}
How are we going to merge the methods into a single one so that when we ask for managers it should return managers and when we need fulltime or part time employees, it should give the same. One terrible approach will be to incorporate all possible parameters. See the code below. But don't do this.
Solution 3 : Bad practice
public List<Employee> filterEmployee(List<Employee> employees, boolean needFulltimeEmployee, boolean needManager,
boolean fulltimeSelectionFlag)
{
List<Employee> filteredEmployees = new ArrayList<>();
for (Employee employee : employees)
{
if ((needFulltimeEmployee ^ !employee.isFulltimeEmployee && fulltimeSelectionFlag)
|| (needManager ^ !employee.isManager && !fulltimeSelectionFlag))
filteredEmployees.add(employee);
}
return filteredEmployees;
}
boolean fulltimeSelectionFlag)
{
List<Employee> filteredEmployees = new ArrayList<>();
for (Employee employee : employees)
{
if ((needFulltimeEmployee ^ !employee.isFulltimeEmployee && fulltimeSelectionFlag)
|| (needManager ^ !employee.isManager && !fulltimeSelectionFlag))
filteredEmployees.add(employee);
}
return filteredEmployees;
}
Just see the client code filterEmployee(employeeList, true, false, true); The client has to be careful on which boolean value he is passing and what if you need additional parameter like filtering employees who are getting fat paychecks.
People can pause here and think of a solution that will address the above issue.
Let us analyse our problem at hand. We have a list of employees and based on some condition we are filtering. The first step would be to create an interface.
Solution 4:
public interface PredicateEmployee
{
boolean test(Employee employee);
}
{
boolean test(Employee employee);
}
Now implement this interface based on the requirement such as manager or fulltimeEmployee.
public class ManagerPredicate implements PredicateEmployee
{@Override
public boolean test(Employee employee)
{
return employee.isManager;
}
}
public class FulltimeEmployeePredicate implements PredicateEmployee
{
@Override
public boolean test(Employee employee)
{
return employee.isFulltimeEmployee;
}
}
The filterEmployee method would be
public List<Employee> filterEmployee(List<Employee> employees, PredicateEmployee p)
{
List<Employee> filteredEmployees = new ArrayList<>();
for(Employee employee : employees)
{
if(p.test(employee))
filteredEmployees.add(employee);
}
return filteredEmployees;
}
{
List<Employee> filteredEmployees = new ArrayList<>();
for(Employee employee : employees)
{
if(p.test(employee))
filteredEmployees.add(employee);
}
return filteredEmployees;
}
Can't get better right? This type of pattern is called strategy pattern. Whenever you have a family of algorithms, you can abstract them away and use the strategy pattern. The client even don't need to implement the class everytime. He can simply use anonymous inner code as below.
filterEmployee(employees, new PredicateEmployee(){ public boolean test(Employee employee)
{return employee.isGettingFatPaycheck}
});
{return employee.isGettingFatPaycheck}
});
But wait. Do we have boiler plate code here? Yes! Except the highlighted text all other codes are just repeating itself just to support the logic. But we can't help with that. As java allows only primitives and object references as method parameters. They are called first citizens. While methods are called second citizens as they can't be passed as method parameters. But not anymore. Java 8 promoted the methods as first class citizens and now they can be passed as parameter. So from java 8 instead of passing the object, just the behavior can be passed like below.
filterEmployee(employees, employee -> employee.isGettingFatPaycheck);
Wow! Now we have found something sweet that is both concise and flexible. That is the potential java 8 is having. The above solution can be made more better by introducing generics. I will leave that to you. Also try to implement a sorting function that will arrange the employees based on different parameters(like age, salary, experience). I will leave that to you. Your comments and feedback are welcome. Kindly comment. Stay tuned for next post.
Regards,
A.S.Balaji.
Nice article . Thank u Balaji
ReplyDeleteNice
ReplyDeleteSuper!
ReplyDelete