Skip to content

svascot/combinator-pattern

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 

Repository files navigation

combinator-pattern

Combinator pattern to implement a validator that allows to create custom validations e.g. Given the object User with some fields. We can implement a full object validation or implement partial validations.

// Business Object to be validated.
class Customer {
    private final String name;
    private final String email;
    private final String phoneNumber;
    private final LocalDate dob;
}    

// Service class.
class Service {

    // Full object validation.
    EnumSet<CustomerValidationResult> result = EnumSetApproachValidator.isEmailValid()
            .and(EnumSetApproachValidator.isPhoneNumberValid())
            .and(EnumSetApproachValidator.isAdult())
            .apply(badCustomer);

    // Partial validation. email and age.
    EnumSet<CustomerValidationResult> result = EnumSetApproachValidator.isEmailValid()
            .and(EnumSetApproachValidator.isAdult())
            .apply(badCustomer);

    // Partial validation. phone and email.
    EnumSet<CustomerValidationResult> result = EnumSetApproachValidator.isEmailValid()
            .and(EnumSetApproachValidator.isPhoneNumberValid())
            .apply(badCustomer);
}

The idea is to create an interface that extends Function<BusinessObject, ValidationResult>, Implement an EnumSet with the states e.g. SUCCESS, FIELD_ONE_NOT_VALID, FIELD_TWO_NOT_VALID implement the business validation in Functions that takes this interface as argument and return the EnumSet state. Also implement a default method and with this interface argument to perform the "next" validation.

public interface EnumSetApproachValidator extends Function<Customer, EnumSet<CustomerValidationResult>> {

    EnumSet<CustomerValidationResult> SUCCESS_ONLY = EnumSet.of(SUCCESS);

    static EnumSetApproachValidator isEmailValid() {
        return customer -> customer.getEmail().contains("@") ?
                SUCCESS_ONLY : EnumSet.of(EMAIL_NOT_VALID);
    }

    static EnumSetApproachValidator isPhoneNumberValid() {
        return customer -> customer.getPhoneNumber().startsWith("+1") ?
                SUCCESS_ONLY : EnumSet.of(PHONE_NUMBER_NOT_VALID);
    }

    default EnumSetApproachValidator and(EnumSetApproachValidator other){
        return customer -> {
            EnumSet<CustomerValidationResult> thisResult = this.apply(customer);
            EnumSet<CustomerValidationResult> otherResult = other.apply(customer);

            if(thisResult.equals(SUCCESS_ONLY)) return otherResult;
            if(otherResult.equals(SUCCESS_ONLY)) return thisResult;

            EnumSet<CustomerValidationResult> combinedResult = EnumSet.copyOf(thisResult);
            combinedResult.addAll(otherResult);
            return combinedResult;
        };
    }

    enum CustomerValidationResult{
        SUCCESS,
        PHONE_NUMBER_NOT_VALID,
        EMAIL_NOT_VALID,
        IS_NOT_AN_ADULT
    }
}

The way to use this validation will be:

EnumSet<CustomerValidationResult> resultBadCostumer = EnumSetApproachValidator.isEmailValid()
                .and(EnumSetApproachValidator.isPhoneNumberValid())
                .and(EnumSetApproachValidator.isAdult())
                .apply(badCustomer);

To execute this project go to Main class and run the main method.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages