I have an interface that looks like this:
public interface InputsValidator<T> {
List<MessageEnum> validator(T input);
}
I have multiple implementations for example:
@Component
class AddressInputsValidator implements InputsValidator<Address> {
@Override
public List<MessageEnum> validator(Address input) {
List<MessageEnum> messages = new ArrayList<>();
// Some actions
return messages;
}
}
and
@Component
class ContactInputsValidator implements InputsValidator<Contact> {
@Override
public List<MessageEnum> validator(Contact input) {
List<MessageEnum> messages = new ArrayList<>();
// Some actions
return messages;
}
}
Now, in my Spring Boot service, I need to call multiple validators. Today, if I need 10 validators I need to declare all of them:
@Component
@RequiredArgsConstructor
class MyServiceApiImpl extends DelegateHelper implements UserApiDelegate {
private final InputsValidator<Address> addressInputsValidator;
private final InputsValidator<Contact> contactInputsValidator;
....
}
Is there an elegant way or design pattern to replace all these InputsValidator declarations of each type, with only generic one? For example
@Component
@RequiredArgsConstructor
class MyServiceApiImpl extends DelegateHelper implements UserApiDelegate {
private final InputsValidator<T> inputsValidator; // Wrong solution
....
}
I'm not sure I understand your question fully, but here are a few ideas.
You can group multiple validators on the same type with a class like this:
public class MultiInputsValidator<T> implements InputsValidator<T> {
private final InputsValidator<T>[] validators;
public MultiInputsValidator(InputsValidator<T>... validators) {
this.validators = validators;
}
@Override
public List<MessageEnum> validator(T input) {
List<MessageEnum> result = new ArrayList<>();
for (InputsValidator<T> tor: validators) {
result.addAll(tor.validator(input));
}
return result;
}
}
You can write a validator class that validates a property of an object as follows:
public class PropertyValidator<T,F> implements InputsValidator<T> {
private final Function<T,F> getter;
private final InputsValidator<F> fieldValidator;
public PropertyValidator(
Function<T,F> getter, InputsValidator<F> fieldValidator) {
this.getter = getter;
this.fieldValidator = fieldValidator;
}
@Override
public List<MessageEnum> validator(T input) {
return fieldValidator.validator(getter.apply(input));
}
}
And you can combine the above for a particular class, like say:
public class Compound {
private Contact contact;
private Address address;
public Contact getContact() {
return contact;
}
public Address getAddress() {
return address;
}
...
}
As follows:
@Component
public class CompoundInputsValidator extends MultiInputsValidator<Compound> {
@Autowired
public CompoundInputsValidator(
AddressInputsValidator addressValidator,
ContactInputsValidator contactValidator) {
super(new PropertyValidator<>(Compound::getAddress,addressValidator),
new PropertyValidator<>(Compound::getContact,contactValidator));
}
}
I hope this will help.