For refactoring to better readability and reduced complexity of your validate method I would apply the Extract Method refactoring. But depending on the available Java version you have different options for default interface methods.
There are different options at hand in Java 8 and Java 9. In both cases you could of course extract methods to abstract interface methods which would need to be implemented by the implementation classes. But I guess you want to keep the default implementation code of the extracted classes inside your interface as well. So I will not address this option here.
Java 8
If you are restricted to Java 8 you can - in addition to the already mentioned public abstract methods - extract methods as either other public default methods or public static methods of your interface.
Here is a simple refactoring approach for extracting methods into public default interface methods:
public interface ApplicationValidatorJava8WithDefaults {
default void validate() {
Application application = application().get();
validateApplication(application);
for (Form form : application.data().forms) {
validateForm(form);
}
}
default void validateApplication(Application application) {
if (StringUtils.isEmpty(application.name()) || application.data() == null) {
throw new IllegalArgumentException();
}
}
default void validateForm(Form form) {
if (StringUtils.isEmpty(form.getId())) {
throw new IllegalArgumentException();
}
for (Question question : form.getQuestions()) {
validateQuestion(question);
}
}
default void validateQuestion(Question question) {
if (StringUtils.isEmpty(question.getQuestion())
|| StringUtils.isEmpty(question.getValue())) {
throw new IllegalArgumentException();
}
}
Application application();
}
Now the same with extracting methods into public static interface methods:
public interface ApplicationValidatorJava8WithStatics {
default void validate() {
Application application = application().get();
validateApplication(application);
for (Form form : application.data().forms) {
validateForm(form);
}
}
static void validateApplication(Application application) {
if (StringUtils.isEmpty(application.name()) || application.data() == null) {
throw new IllegalArgumentException();
}
}
static void validateForm(Form form) {
if (StringUtils.isEmpty(form.getId())) {
throw new IllegalArgumentException();
}
for (Question question : form.getQuestions()) {
validateQuestion(question);
}
}
static void validateQuestion(Question question) {
if (StringUtils.isEmpty(question.getQuestion())
|| StringUtils.isEmpty(question.getValue())) {
throw new IllegalArgumentException();
}
}
Application application();
}
Java 9
If you can use Java 9 you can - in addition to all the already mentioned options methods - extract methods as either private instance methods or private static methods of your interface.
Here is a simple refactoring approach for extracting methods into private instance interface methods:
public interface ApplicationValidatorJava9WithPrivateInstanceMethods {
default void validate() {
Application application = application().get();
validateApplication(application);
for (Form form : application.data().forms) {
validateForm(form);
}
}
private void validateApplication(Application application) {
if (StringUtils.isEmpty(application.name()) || application.data() == null) {
throw new IllegalArgumentException();
}
}
private void validateForm(Form form) {
if (StringUtils.isEmpty(form.getId())) {
throw new IllegalArgumentException();
}
for (Question question : form.getQuestions()) {
validateQuestion(question);
}
}
private void validateQuestion(Question question) {
if (StringUtils.isEmpty(question.getQuestion())
|| StringUtils.isEmpty(question.getValue())) {
throw new IllegalArgumentException();
}
}
Application application();
}
Now the same with extracting methods into private static interface methods:
public interface ApplicationValidatorJava9WithPrivateStaticMethods {
default void validate() {
Application application = application().get();
validateApplication(application);
for (Form form : application.data().forms) {
validateForm(form);
}
}
private static void validateApplication(Application application) {
if (StringUtils.isEmpty(application.name()) || application.data() == null) {
throw new IllegalArgumentException();
}
}
private static void validateForm(Form form) {
if (StringUtils.isEmpty(form.getId())) {
throw new IllegalArgumentException();
}
for (Question question : form.getQuestions()) {
validateQuestion(question);
}
}
private static void validateQuestion(Question question) {
if (StringUtils.isEmpty(question.getQuestion())
|| StringUtils.isEmpty(question.getValue())) {
throw new IllegalArgumentException();
}
}
Application application();
}
Note: I also removed the first else branch as it is obsolete after throwing an exception in the first if clause. Good IDEs (such as IntelliJ) will already inform you about that and remove it for you with a single command. This already improves complexity as the overall nesting of the method is already reduced by one level.