# Chain of Responsibility Design Pattern explained in 2 minutes

### Problem Statement

In real-world applications, we frequently have to run a series of validations to ensure our model class is properly created before we persist it in our database. Consider the below `Employee` class

```java
public record Employee(int employeeId, String firstName, String lastName, int salary, int managerId, int age) {
}
```

If we have to run validations to ensure whether the names are properly set, the age is valid or a valid employee ID has been assigned before we persist our POJO into a database, what's the simplest way?

Presenting the humble `If/else`!

```java
  if (validEmployee.employeeId() != 0 
        && !validEmployee.firstName().isEmpty() 
        && !validEmployee.lastName().isEmpty() 
        && validEmployee.age() >= 18) {
      return true;
  }
```

What red flag do you see in the above code? Although it's simpler, it violates OCP (Open/Closed Principle). The code is not extensible and will require frequent modifications.

Chain of Responsibility Design Pattern can help tidy up the code.

```java
public abstract class Validator {
    public Validator nextValidator;

    public Validator setNextValidator(Validator next) {
        this.nextValidator = next;
        return this;
    }

    public abstract boolean isValid(Employee employee);
}

public class EmployeeIdValidator extends Validator {
    @Override
    public boolean isValid(Employee employee) {
        System.out.println("Running Employee ID Validator");
        if (nextValidator == null) {
            // if there is no next validator in the chain
            return isIdValid(employee.employeeId());
        } else if (isIdValid(employee.employeeId())) {
            // delegate to the next validator
            return nextValidator.isValid(employee);
        } else {
            System.out.println("Employee ID is invalid");
            return false;
        }
    }

    private boolean isIdValid(int id) {
        return id != 0;
    }
}

public class NameValidator extends Validator {
    @Override
    public boolean isValid(Employee employee) {
        System.out.println("Running Name Validator");
        if (nextValidator == null) {
            return isNameValid(employee);
        } else if (isNameValid(employee)) {
            return nextValidator.isValid(employee);
        } else {
            System.out.println("Employee name is invalid");
            return false;
        }
    }

    private static boolean isNameValid(Employee employee) {
        return !employee.firstName().isBlank()
                && !employee.firstName().isEmpty()
                && !employee.lastName().isEmpty()
                && !employee.lastName().isBlank();
    }
}

public class AgeValidator extends Validator {
    @Override
    public boolean isValid(Employee employee) {
        System.out.println("Running Age Validator");
        if (nextValidator == null) {
            return isAgeValid(employee.age());
        } else if (isAgeValid(employee.age())) {
            return nextValidator.isValid(employee);
        } else {
            System.out.println("Age is not valid");
            return false;
        }
    }

    private boolean isAgeValid(int age) {
        return age >= 18 && age <= 70;
    }
}

public class Main {
    public static void main(String[] args) {
        testChainOfResponsibility();
    }

    public static void testChainOfResponsibility() {
        Employee validEmployee = new Employee(1, "Snehasish", "Roy", 100, 100, 20);
        // Chain the validators
        Validator validatorChain = new EmployeeIdValidator()
                .setNextValidator(new AgeValidator()
                        .setNextValidator(new NameValidator()));
        System.out.println(validatorChain.isValid(validEmployee));

        Employee invalidEmployee = new Employee(1, "Snehasish", "Roy", 100, 100, 10);
        System.out.println(validatorChain.isValid(invalidEmployee));
    }
}

// Output
Running Employee ID Validator
Running Age Validator
Running Name Validator
true

Running Employee ID Validator
Running Age Validator
Age is not valid
false
```

In the code above, we created dedicated validators for handling only one validation at a time. Each validator first performs local validations and then delegates to the next validator (*if any*).

### What's the benefit?

* Follows OCP - Any modification/extension in the validation logic of a validator will require a change in only one class.
    
* Follows SRP (Single Responsibility Principle) - Each validator performs only one task.
    

### What's the drawback?

* Validators must be chained correctly - if there are any cycles in the chain, then it can cause issues at runtime.
    
* The responsibility of initializing validators lies with the client. The client can either create a chain at the compile time or dynamically update the chain as per the business logic.
