# Validator Design Pattern


The Validator Pattern is a **simple** structural Design Pattern that aids in maintaining code organization, separation, simplicity, and maintainability.  First, I will show you how you may write code without using the validator pattern. Afterwards, I will illustrate how I prefer to leverage this structural design pattern to have cleaner code.

# Without Validator Pattern

It is crucial in backend development to verify the user input before processing it. Without using the validator pattern, you may implement the verification logic in your controller. This is ok for small verifications, but this could get messy pretty quickly. If you have a lot of verifications, your code could look like this.

```java
    @Autowired
    private CatService catService;

    @PostMapping("/add")
    public Boolean addCat(AddCatRequest request){

        if(request.getId() != null){
            throw new IllegalArgumentException("Id must be null");
        }

        if(StringUtils.isBlank(request.getName())){
            throw new IllegalArgumentException("Name cannot be blank");
        }

        if(request.getAge() == null || request.getAge() < 0){
            throw new IllegalArgumentException("Age cannot be null");
        }

        if(request.getBirthday() == null){
            throw new IllegalArgumentException("Birthday cannot be null");
        }

        if(request.getRegisterDay() == null){
            throw new IllegalArgumentException("RegisterDay cannot be null");
        }

        if(request.getBirthday().after(request.getRegisterDay())){
            throw new IllegalArgumentException("Birthday cannot be after registerDay");
        }

        return catService.addCat(request);
    }
```

This single method, 'add', already consists of **28 lines** of code. Considering that we will likely incorporate additional methods like 'update' and 'delete', our controller is rapidly accumulating a significant amount of code and becomes **messy**.

# Validator Pattern

With the validator pattern, we separate the **validation** **logic** from the controller logic into a separate file. Therefore our test is **cleaner** and better **testable**. The `add` method in our main controller now uses just two lines of code. We use `addValidator.validate(request)` to do the validation for us.

```java
    @Autowired
    private CatService catService;
    
    @Autowired
    private AddValidator addValidator;

    @PostMapping("/add")
    public Boolean addCat(AddCatRequest request){
        addValidator.validate(request);
        return catService.addCat(request);
    }
```

The validation logic is outsourced into a separate `AddValidator.java` file.

```java
@Service
public class AddValidator {

    public void validate(AddCatRequest request){

        if(request.getId() != null){
            throw new IllegalArgumentException("Id must be null");
        }

        if(StringUtils.isBlank(request.getName())){
            throw new IllegalArgumentException("Name cannot be blank");
        }

        if(request.getAge() == null || request.getAge() < 0){
            throw new IllegalArgumentException("Age cannot be null");
        }

        if(request.getBirthday() == null){
            throw new IllegalArgumentException("Birthday cannot be null");
        }

        if(request.getRegisterDay() == null){
            throw new IllegalArgumentException("RegisterDay cannot be null");
        }

        if(request.getBirthday().after(request.getRegisterDay())){
            throw new IllegalArgumentException("Birthday cannot be after registerDay");
        }

    }
}
```
# Also supports Polymorphism
Another **great advantage** of this design pattern is, it perfectly supports `Polymorphism`. Let's say we have different kind of cats and our `AddCatRequest` will be changed to an `abstract class`. The `concrete classes` are `AddSiameseRequest` and `AddRagDollRequest`. With our **validation pattern**, we can easily use the same controller logic and add `different validators` to handle each and every cat specifically.

First we are adding our folder structure as follows
```
├── controller/
│   └── CatController.java
└── validators/
    ├── handler/
    │   ├── ragdoll/
    │   │   └── RagDollAddValidator.java
    │   └── siamese/
    │       └── SiameseAddValidator.java
    ├── router/
    │   └── ValidationRouter.java
    └── AbstractAddValidator.java
```
We are creating an abstract class `AbstractAddValidator.java`, which contains a `validate()` method
```java
public abstract class AbstractAddValidator{
    public abstract void validate(AddCatRequest request);
}
```
Now we can implement concrete classes `RagDollAddValidator.java` and `SiameseAddValidator.java` for handling the specific `CatType`. 
```java
@Service
public class RagDollAddValidator extends AbstractAddValidator {

    @Override
    public void validate(AddCatRequest request){

        if(request.getType().equals(CatType.RAGDOLL)){
            throw new IllegalArgumentException("CatType is not supported");
        }

        if(request.getId() != null){
            throw new IllegalArgumentException("Id must be null");
        }

        if(StringUtils.isBlank(request.getName())){
            throw new IllegalArgumentException("Name cannot be blank");
        }

        if(request.getAge() == null || request.getAge() < 0){
            throw new IllegalArgumentException("Age cannot be null");
        }

        if(request.getBirthday() == null){
            throw new IllegalArgumentException("Birthday cannot be null");
        }

        if(request.getRegisterDay() == null){
            throw new IllegalArgumentException("RegisterDay cannot be null");
        }

        if(request.getBirthday().after(request.getRegisterDay())){
            throw new IllegalArgumentException("Birthday cannot be after registerDay");
        }
    }
}
```
```java
@Service
public class SiameseAddValidator extends AbstractAddValidator {

    @Override
    public void validate(AddCatRequest request) {

        if(request.getType().equals(CatType.SIAMESE)){
            throw new IllegalArgumentException("CatType is not supported");
        }

        if(request.getId() != null){
            throw new IllegalArgumentException("Id must be null");
        }

        if(StringUtils.isBlank(request.getName())){
            throw new IllegalArgumentException("Name cannot be blank");
        }

        if(request.getAge() == null || request.getAge() < 0){
            throw new IllegalArgumentException("Age cannot be null");
        }

        if(request.getBirthday() == null){
            throw new IllegalArgumentException("Birthday cannot be null");
        }

        if(request.getRegisterDay() == null){
            throw new IllegalArgumentException("RegisterDay cannot be null");
        }

        if(request.getBirthday().after(request.getRegisterDay())){
            throw new IllegalArgumentException("Birthday cannot be after registerDay");
        }
    }
}
```
Now we need a `ValidationRouter.java`, which is responsible for returning the correct `handler`, based on the requests `CatType`.
```java
@Service
public class ValidationRouter {

    public AbstractAddValidator getValidator(AddCatRequest request){
        if(request.getType().equals(CatType.RAGDOLL)){
            return new RagDollAddValidator();
        }
        if(request.getType().equals(CatType.SIAMESE)){
            return new SiameseAddValidator();
        }

        throw new IllegalArgumentException("CatType not supported");
    }
}
```
Our `CatController.java` is still very tidy. The only thing we have to do now, is injecting the `ValidationRouter` to get the `AbstractAddValidator` and then call `validate()`.
 
```java
    @Autowired
    private CatService catService;

    @Autowired
    private ValidationRouter router;

    @PostMapping("/add")
    public Boolean addCat(AddCatRequest request){
        AbstractAddValidator addValidator = router.getValidator(request);
        addValidator.validate(request);
        return catService.addCat(request);
    }
```

# Conclusion

In this article, I have written down one of my favorite simple yet highly useful structural design patterns. We all appreciate clean and well-organized code that is easy to maintain and testable. The **validation pattern** offers an ideal approach of validating your requests and allowing for clear separation from the business logic.
Additionally, we have seen how the validator pattern embraces **Polymorphism**, thus adhering to the Open-Closed Principle in Software Engineering.


