3

I have a builder pattern in which most likely all my parameters are going to be mandatory so I have created a long constructor as shown below in the code.

public final class ResponseHolder {

    // all below six are related to response information
    private final String response;
    private final boolean isLinking;
    private final TypeHold typeOfId;
    private final long userTimeInMs;
    private final long userLmdInDays;
    private final String maskInfo;

    // below two are related to error handling
    private final ErrorCode error;
    private final StatusCode status;

    private ResponseHolder(Builder builder) {
        this.response = builder.response;
        this.isLinking = builder.isLinking;
        this.typeOfId = builder.typeOfId;
        this.userTimeInMs = builder.userTimeInMs;
        this.userLmdInDays = builder.userLmdInDays;
        this.maskInfo = builder.maskInfo;
        this.error = builder.error;
        this.status = builder.status;
    }

    public static class Builder {
        protected final String response;
        protected final TypeHold typeOfId;
        protected final String maskInfo;
        protected final ErrorCode error;
        protected final StatusCode status;
        protected final boolean isLinking;
        protected final long userTimeInMs;
        protected final long userLmdInDays;


        public Builder(String response, TypeHold typeOfId, String maskInfo, ErrorCode error,
                StatusCode status, boolean isLinking, long userTimeInMs, long userLmdInDays) {
            this.response = response;
            this.typeOfId = typeOfId;
            this.maskInfo = maskInfo;
            this.error = error;
            this.status = status;
            this.isLinking = isLinking;
            this.userTimeInMs = userTimeInMs;
            this.userLmdInDays = userLmdInDays
        }

        public ResponseHolder build() {
            return new ResponseHolder(this);
        }
    }

    // getters here
}

Now I am confuse when all the parameters are mandatory, then how it can be useful? Is there any better way of representing my above Builder pattern? May be logically grouping the parameters being passed in to their own classes to reduce the number of parameters being passed to the builder constructor?

While having separate objects simplifies things quite a bit, it also makes things a little difficult to follow if one is not familiar with the code. One thing I can do is moving all parameters into their own addParam(param) methods and then performing validation on required parameters in the build() method at runtime?

What is best practice I should follow here and is there any better approach that I can use here?

john
  • 11,311
  • 40
  • 131
  • 251
  • 3
    You have asked the same question yesterday, I not sure why would ask it again. – YoungHobbit Dec 26 '15 at 05:24
  • I wouldn't even call this the builder pattern. @Andreas' answer is how builders are generally written. – Louis Wasserman Dec 26 '15 at 05:35
  • 3
    David, if my answer wasn't satisfying to you (referring to your previous question, which I answered), you could comment on my answer, allowing me to update my answer to better fit your situation. But you didn't comment on my answer, you showed it no attention, then decided to write a new question. Not cool – Vince Dec 26 '15 at 07:57

2 Answers2

8

The Builder pattern shines when there are many different valid parameter permutations that allow you to create an object. Without the Builder pattern, you would be forced to create many ugly and confusing constructors to handle all the possible valid parameter combinations.

But in your case, there is only one valid set of parameters that allows you to create your object. This is exactly what a constructor is for. Using the Builder pattern here is not only overkill, it simply is not appropriate.

Just use a normal constructor for your ResponseHolder class.

sstan
  • 35,425
  • 6
  • 48
  • 66
  • having normal constructor with a big line for ResponseHolder will also look ugly? Is there any other way I can do some decomposition may be either in builder pattern or in constructor that can make it simple? – john Dec 26 '15 at 05:21
  • @david Yes, see my answer. – Andreas Dec 26 '15 at 05:28
  • 2
    A constructor with multiple params does not HAVE to look ugly, that depends on your code formatting. My recommendation at that point is to put the first param on the same line as the constructor, then line up the rest of the params under it. This can help with both readability (since you can comment after each part) but also simplifying code since you don't have to add extra 'builder' logic/code into your class, etc. and the end user can use auto-complete functionality of their IDE to help complete the constructor.) – mawalker Dec 26 '15 at 05:29
7

The purpose of the builder pattern is to have a no-arg constructor, many well-named setter methods, and a final completion method, which validates that combination of values given are valid, before constructing the target object.

In your case, since all values are required, the main purpose of the builder pattern is to provide a form of named-parameter support.

Also, your target object should have many-arg constructor, rather than taking builder object as argument)

So, your builder should be (assuming no null values allowed):

public static class Builder {
    private String     response;
    private TypeHold   typeOfId;
    private String     maskInfo;
    private ErrorCode  error;
    private StatusCode status;
    private Boolean    isLinking;
    private Long       userTimeInMs;
    private Long       userLmdInDays;

    public Builder setResponse(String response) {
        this.response = response;
        return this;
    }
    public Builder setTypeOfId(TypeHold typeOfId) {
        this.typeOfId = typeOfId;
        return this;
    }
    public Builder setMaskInfo(String maskInfo) {
        this.maskInfo = maskInfo;
        return this;
    }
    public Builder setError(ErrorCode error) {
        this.error = error;
        return this;
    }
    public Builder setStatus(StatusCode status) {
        this.status = status;
        return this;
    }
    public Builder setIsLinking(boolean isLinking) {
        this.isLinking = isLinking;
        return this;
    }
    public Builder setUserTimeInMs(long userTimeInMs) {
        this.userTimeInMs = userTimeInMs;
        return this;
    }
    public Builder setUserLmdInDays(long userLmdInDays) {
        this.userLmdInDays = userLmdInDays;
        return this;
    }
    public ResponseHolder build() {
        if (this.response == null ||
            this.typeOfId == null ||
            this.maskInfo == null ||
            this.error == null ||
            this.status == null ||
            this.isLinking == null ||
            this.userTimeInMs == null ||
            this.userLmdInDays == null) {
            throw new IllegalStateException("Not all required values given");
        }
        return new ResponseHolder(this.response,
                                  this.typeOfId,
                                  this.maskInfo,
                                  this.error,
                                  this.status,
                                  this.isLinking,
                                  this.userTimeInMs,
                                  this.userLmdInDays);
    }
}

You can now use it like this:

ResponseHolder holder = new ResponseHolder.Builder()
                        .setResponse(response)
                        .setTypeOfId(typeOfId)
                        .setMaskInfo(maskInfo)
                        .setError(error)
                        .setStatus(status)
                        .setIsLinking(isLinking)
                        .setUserTimeInMs(userTimeInMs)
                        .setUserLmdInDays(userLmdInDays)
                        .build();
Andreas
  • 154,647
  • 11
  • 152
  • 247