Filter Design Pattern

The Filter pattern allows us to filter a collection of objects using various conditions(filters) and chaining them in a decoupled way through logical operations. This design pattern is useful when we want to select subset of objects that satisfies one or multiple conditions. It comes under structural pattern.

Advantages of Filter Pattern

  • It provides a way to filter objects based on certain criteria.
  • We can add a new filter any time without affecting client's code.
  • We can select filters dynamically during program execution.

For Example: Let, EmployeeList be the list of all employees of a company and we want to select all Male employees whose salary is between 1000 to 5000. In this case we need three filters.
Male Filter : Returns List of all Male employees.
LessThanFilter : Returns List of employees whose salary is less than 5000.
GreaterThanFilter : Returns List of employees whose salary is greater than 1000.

Now, we have to perform logical And of above mentioned three filters. Let's see how filter pattern can help us in solving this problem.

Implementation of Filter Design Pattern

Employee.java
public class Employee {
    private String name;
    private String gender;
    private String department;
    private int salary;

    public Employee(String name, String gender, String department, int salary){
        this.name = name;
        this.gender = gender;
        this.department = department;
        this.salary = salary;
    }

    public String getName() {
     return name;
    }
    
    public String getGender() {
        return gender;
    }
 
    public String getDepartment() {
        return department;
    }
    
    public int getSalary(){
     return salary;
    }
}

We will define Filter interface having checkCondition method which takes a List of employees as input and then returns a subset of employees who satisfy the filter criteria. Every filter criteria class must implement this interface.

Filter.java
import java.util.List;

public interface Filter {
    public List<Employee> checkCondition(List<Employee> employees);
}

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

public class GenderFilter implements Filter {
    private String gender;
 
    public GenderFilter(String gender){
        this.gender = gender;
    }
 
    @Override
    public List<Employee> checkCondition(List<Employee> employees){
        List<Employee> filterredEmployees = new ArrayList<Employee>();
        for(Employee e : employees){
            if(e.getGender().equalsIgnoreCase(gender)){
                filterredEmployees.add(e);
            }
        }
        return filterredEmployees;
    }
}

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

public class DepartmentFilter implements Filter {
    private String department;
 
    public DepartmentFilter(String department){
        this.department = department;
    }
 
    @Override
    public List<Employee> checkCondition(List<Employee> employees){
        List<Employee> filterredEmployees = new ArrayList<Employee>();
        for(Employee e : employees){
            if(e.getDepartment().equalsIgnoreCase(department)){
                filterredEmployees.add(e);
            }
        }     
        return filterredEmployees;
    }
}

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

public class SalaryGreaterThanFilter implements Filter {
    private int value ;
 
    public SalaryGreaterThanFilter(int value){
        this.value = value;
    }
 
    @Override
    public List<Employee> checkCondition(List<Employee> employees){
        List<Employee> filterredEmployees = new ArrayList<Employee>();
        for(Employee e : employees){
            if(e.getSalary() > value){
                filterredEmployees.add(e);
            }
        }
        return filterredEmployees;
    }
}

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

public class SalaryLessThanFilter implements Filter {
    private int value ;
 
    public SalaryLessThanFilter(int value){
        this.value = value;
    }
 
    @Override
    public List<Employee> checkCondition(List<Employee> employees){
        List<Employee> filterredEmployees = new ArrayList<Employee>();
        for(Employee e : employees){
            if(e.getSalary() < value){
                filterredEmployees.add(e);
            }
        }
        return filterredEmployees;
    }
}

AndFilter.java takes a List of Filters and then perform the logical and of all filters. The output of one filter becomes the input of other in chained manner. It is used for implementing composite criteria like, "List of employees who are Male and works in CSE department".

AndFilter.java
import java.util.List;

public class AndFilter implements Filter {
    private List<Filter> filterList;
 
    public AndFilter(List<Filter> filterList){
        this.filterList = filterList;
    }
 
    @Override
    public List<Employee> checkCondition(List<Employee> employees){
        List<Employee> filterredEmployees = employees;
        for(Filter filter : filterList){
            filterredEmployees = filter.checkCondition(filterredEmployees);
        }
        return filterredEmployees;
    }
}

FilterPatternExample.java first creates a list of employees and the filter them using specific filter classes to gets subset of employees satisfying particular condition(s).

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

public class FilterPatternExample {
    public static void main(String[] args) {
        List<Filter> filterList = new ArrayList<Filter>();
        List<Employee> employeeList= new ArrayList<Employee>();
        employeeList.add(new Employee("Jack", "Male", "CSE", 1000));
        employeeList.add(new Employee("Rose", "Female", "ECE", 2000));
        employeeList.add(new Employee("George", "Male", "CSE", 3000));
        employeeList.add(new Employee("Mike", "Male", "ECE", 4000));
        employeeList.add(new Employee("Julia", "Female", "CSE", 5000));
     
        // Define various filters
        Filter departmentFilter = new DepartmentFilter("CSE");
        Filter maleFilter = new GenderFilter("Male");
        Filter lessThanFlter = new SalaryLessThanFilter(5000);
        Filter greaterThanFilter = new SalaryGreaterThanFilter(1000);
     
        System.out.println("List of Male Employees");
            printEmployeesName(maleFilter.checkCondition(employeeList));
     
        System.out.println("List of CSE Employees");
            printEmployeesName(departmentFilter.checkCondition(employeeList));
     
        System.out.println("List of Male Employees whose salary is" + 
            " greater than 1000 and less than 5000");
        filterList.add(lessThanFlter);
        filterList.add(greaterThanFilter);
        filterList.add(maleFilter);
        Filter andFilter = new AndFilter(filterList);
        printEmployeesName(andFilter.checkCondition(employeeList));     
    }
    
    private static void printEmployeesName(List<Employee> empList){
        for(Employee e : empList){
            System.out.print(e.getName() + ", ");
        }
        System.out.println("\n");
    }
}

Output

List of Male Employees
Jack, George, Mike, 

List of CSE Employees
Jack, George, Julia, 

List of Male Employees whose salary is greater than 1000 and less than 5000
George, Mike,

Related Topics
Strategy Design Pattern
Builder Design Pattern
Abstract Factory Design Pattern
Flyweight Design Pattern
Facade Design Pattern
Command Design Pattern
Memento Design Pattern
Mediator Design Pattern
Observer Design Pattern
Singleton Design Pattern
List of Design Patterns