Arthuzad
Arthuzad

Reputation: 73

Combinator pattern

I'm having a little issue with the result I'm getting with 'Combinator' pattern.

public interface CustomerRegistrationValidator 
    extends Function<Customer, ValidationResult> {

    static CustomerRegistrationValidator isEmailValid() {
        return customer -> customer.getEmail().contains("@") ? 
                    ValidationResult.SUCCESS : ValidationResult.EMAIL_NOT_VALID;
    }

    static CustomerRegistrationValidator isPhoneNumberValid() {
        return customer -> customer.getPhoneNumber().startsWith("+0") ?  
                    ValidationResult.SUCCESS : ValidationResult.PHONE_NUMBER_NOT_VALID;
    }

    static CustomerRegistrationValidator isAnAdult() {
        return customer -> Period.between(customer.getDob(), LocalDate.now()).getYears() > 16 ?  
                    ValidationResult.SUCCESS : ValidationResult.IS_NOT_AN_ADULT;
    }

    default CustomerRegistrationValidator and (CustomerRegistrationValidator other) {
        return customer -> {
            ValidationResult result = this.apply(customer);

            return result.equals(ValidationResult.SUCCESS) ? other.apply(customer) : result;
        };
    }

    enum ValidationResult {
        SUCCESS,
        PHONE_NUMBER_NOT_VALID,
        EMAIL_NOT_VALID,
        IS_NOT_AN_ADULT
    }

}

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

            Customer customer = new Customer(
                    "Alice", 
                    "alicegmail.com", 
                    "089877878", 
                    LocalDate.of(2000, 1, 1)
            );

            ValidationResult result = CustomerRegistrationValidator.isEmailValid()
                .and(CustomerRegistrationValidator.isPhoneNumberValid())
                .and(CustomerRegistrationValidator.isAnAdult())
                .apply(customer);

            System.out.println(result);

            if (result != ValidationResult.SUCCESS) {
                throw new IllegalStateException(result.name());
            }           
    }
}

Let's say if there are two errors from those methods(isAnAdult, isEmailValid).Why does it print only one from enum types, in my case 'EMAIL_NOT_VALID' with exception instead of two and by the second one I mean 'IS_NOT_AN_ADULT' ?

Upvotes: 1

Views: 2703

Answers (2)

Maulik Davra
Maulik Davra

Reputation: 1

In this modified code, the CustomerRegistrationValidator interface now returns a List<CustomerRegistrationValidator.ValidationResult> instead of a single ValidationResult. Each validator method creates an empty list of errors and adds the relevant error to the list if the validation fails.

The and method has been updated to accumulate errors from both the current validator and the other validator. It creates a new list of errors, applies the current validator (this), and adds any errors to the list. Then it applies the other validator and adds any errors from that validator as well.

Now, when you apply the validators to a customer, you will get a list of errors instead of a single error. You can then handle the errors accordingly, such as printing them or performing any other necessary actions.

import java.time.LocalDate;
import java.time.Period;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

public interface CustomerRegistrationValidator extends Function<Customer, List<CustomerRegistrationValidator.ValidationResult>> {

    static CustomerRegistrationValidator isEmailValid() {
        return customer -> {
            List<CustomerRegistrationValidator.ValidationResult> errors = new ArrayList<>();
            if (!customer.getEmail().contains("@")) {
                errors.add(CustomerRegistrationValidator.ValidationResult.EMAIL_NOT_VALID);
            }
            return errors;
        };
    }

    static CustomerRegistrationValidator isPhoneNumberValid() {
        return customer -> {
            List<CustomerRegistrationValidator.ValidationResult> errors = new ArrayList<>();
            if (!customer.getPhoneNumber().startsWith("+0")) {
                errors.add(CustomerRegistrationValidator.ValidationResult.PHONE_NUMBER_NOT_VALID);
            }
            return errors;
        };
    }

    static CustomerRegistrationValidator isAnAdult() {
        return customer -> {
            List<CustomerRegistrationValidator.ValidationResult> errors = new ArrayList<>();
            if (Period.between(customer.getDob(), LocalDate.now()).getYears() <= 16) {
                errors.add(CustomerRegistrationValidator.ValidationResult.IS_NOT_AN_ADULT);
            }
            return errors;
        };
    }

    default CustomerRegistrationValidator and(CustomerRegistrationValidator other) {
        return customer -> {
            List<CustomerRegistrationValidator.ValidationResult> errors = new ArrayList<>();
            errors.addAll(this.apply(customer));
            errors.addAll(other.apply(customer));
            return errors;
        };
    }

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



Customer customer = new Customer("Alice", "alicegmail.com", "089877878", LocalDate.of(2000, 1, 1));

List<CustomerRegistrationValidator.ValidationResult> errors = CustomerRegistrationValidator.isEmailValid()
        .and(CustomerRegistrationValidator.isPhoneNumberValid())
        .and(CustomerRegistrationValidator.isAnAdult())
        .apply(customer);

if (!errors.isEmpty()) {
    for (CustomerRegistrationValidator.ValidationResult error : errors) {
        System.out.println(error);
    }
    // Handle the errors accordingly
} else {
    // Registration is successful
}

Upvotes: 0

Andreas
Andreas

Reputation: 159114

If you want a validator to be able to return multiple distinct result codes (enums), then you need to change the return type to Set<ValidationResult>, or more specifically an EnumSet.

E.g. something like this:

interface CustomerRegistrationValidator
    extends Function<Customer, EnumSet<ValidationResult>> {

    static final EnumSet<ValidationResult> SUCCESS_ONLY = EnumSet.of(ValidationResult.SUCCESS);

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

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

    static CustomerRegistrationValidator isAnAdult() {
        return customer -> Period.between(customer.getDob(), LocalDate.now()).getYears() > 16 ?  
                    SUCCESS_ONLY : EnumSet.of(ValidationResult.IS_NOT_AN_ADULT);
    }

    default CustomerRegistrationValidator and(CustomerRegistrationValidator other) {
        return customer -> {
            EnumSet<ValidationResult> thisResult = this.apply(customer);
            EnumSet<ValidationResult> otherResult = other.apply(customer);
            if (thisResult.equals(SUCCESS_ONLY))
                return otherResult;
            if (otherResult.equals(SUCCESS_ONLY))
                return thisResult;
            EnumSet<ValidationResult> combinedResult = EnumSet.copyOf(thisResult);
            combinedResult.addAll(otherResult);
            return combinedResult;
        };
    }

}

Upvotes: 5

Related Questions