Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Abstract class for builder pattern

I have existing model classes that always use builder pattern like this:

public class Model {
    public static class Builder {
        private boolean isValid;
        private List<String> errorMessagesOrNull;

        public Builder setIsValid(final boolean isValid) {
            this.isValid = isValid;
            return this;
        }

        public Builder setErrorMessages(final List<String> errorMessages) {
            this.errorMessagesOrNull = errorMessages;
            return this;
        }

        public List<String> getErrorMessages() {
            return this.errorMessagesOrNull == null ? new ArrayList<>() : this.errorMessagesOrNull;
        }

        public Model Build() {
            return new Model(this);
        }
    }

    private boolean isValid;
    private List<String> errorMessages;

    private Model(final Builder builder) {
        this.isValid = builder.isValid;
        this.errorMessages = builder.getErrorMessages();
    }

    public boolean getIsValid() {
        return isValid;
    }

    public List<String> getErrorMessages() {
        return errorMessages;
    }
}

As you see, the model classes always have isValid and errorMessages. I want to write an abstract class to minimize the repeated logic for those model classes.

So I came up like this abstract class:

public abstract class AbstractModel<T extends AbstractModel<T>> {

    public static abstract class Builder<T> {
        private boolean isValid;
        private List<String> errorMessagesOrNull;

        public Builder<T> setIsValid(final boolean isValid) {
            this.isValid = isValid;
            return this;
        }

        public Builder<T> setErrorMessages(final List<String> errorMessages) {
            this.errorMessagesOrNull = errorMessages;
            return this;
        }

        public List<String> getErrorMessages() {
            return this.errorMessagesOrNull == null ? new ArrayList<>() : this.errorMessagesOrNull;
        }

        public abstract T Build();
    }

    private boolean isValid;
    private List<String> errorMessages;

    private AbstractModel(final Builder<T> builder) {
        this.isValid = builder.isValid;
        this.errorMessages = builder.getErrorMessages();
    }

    public boolean getIsValid() {
        return isValid;
    }

    public List<String> getErrorMessages() {
        return errorMessages;
    }
}

But it's not really working as I intended. When I extends the abstract class:

public class Model extends AbstractModel<Model> {
    // Empty here since all fields are extended
}

I cannot do something like:

Model model = new Model.Builder.setIsValid(true).Build();

I want the abstract class has Builder static class, so that I don't need to write the static class Builder every time.

Please advise.

like image 788
KimchiMan Avatar asked Jan 28 '23 03:01

KimchiMan


1 Answers

You also need to implement the Builder.

public class Model extends AbstractModel<Model>{


    private Model(final Builder builder) {
        super(builder);
    }

    public static class Builder2 extends AbstractModel.Builder<Model> {

        @Override
        public Model Build() {
            return new Model(this);
        }
    }
}

then it possible to call

Model model = new Model.Builder2().Build();

EDIT

Also, the constructor of AbstractBuilder also must be protected.

  protected AbstractModel(final Builder<? extends Builder<T>> builder) {
        this.isValid = builder.isValid;
        this.errorMessages = builder.getErrorMessages();
    }
like image 89
dehasi Avatar answered Jan 30 '23 15:01

dehasi