How to use default value in the builder pattern if that value is not passed and also make thread safe?

11,774

Solution 1

Just initialize the timeout to 500 in the Builder. The timeout method will overwrite this value if it's called.

protected long timeout = 500L;

Solution 2

You can initialize the timeout value in the Builder constructor (or at variable declaration), e.g.

public Builder(long userId, long clientId) {
    this.userId = userId;
    this.clientId = clientId;
    this.timeout = 500L;
}

This way, if you don't call the timeout() method, it will have the default value, otherwise it will be set to the value passed via timeout().

Regarding thread safety, if the Builder constructor is only used for creating a new InputKeys object (as is in the scenario described in the question), without any other area of the code having access to that InputKeys object, there is no need to apply any extra protection.

Share:
11,774
AKIWEB
Author by

AKIWEB

Updated on June 09, 2022

Comments

  • AKIWEB
    AKIWEB almost 2 years

    I am trying to use Builder Pattern for my class..

    Below is my Builder class which I have built by following Joshua Bloch version as he showed in Effective Java, 2nd Edition. Our customer will mostly pass userId, clientId always but other fields are optional and they may or may not pass it. Here Preference is an ENUM class having four fields in it.

    public final class InputKeys {
    
        private long userid;
        private long clientid;
        private long timeout = 500L;
        private Preference pref;
        private boolean debug;
        private Map<String, String> parameterMap;
    
        private InputKeys(Builder builder) {
            this.userid = builder.userId;
            this.clientid = builder.clientId;
            this.pref = builder.preference;
            this.parameterMap = builder.parameterMap;
            this.timeout = builder.timeout;
            this.debug = builder.debug;
        }
    
        public static class Builder {
            protected final long userId;
            protected final long clientId;
            protected long timeout;
            protected Preference preference;
            protected boolean debug;
            protected Map<String, String> parameterMap;
    
            public Builder(long userId, long clientId) {
                this.userId = userId;
                this.clientId = clientId;
            }
    
            public Builder parameterMap(Map<String, String> parameterMap) {
                this.parameterMap = parameterMap;
                return this;
            }
    
            public Builder preference(Preference preference) {
                this.preference = preference;
                return this;
            }
    
            public Builder debug(boolean debug) {
                this.debug = debug;
                return this;
            }
    
            public Builder timeout(long timeout) {
                this.timeout = timeout;
                return this;
            }
    
            public InputKeys build() {
                return new InputKeys(this);
            }
        }
    }
    

    Below is my ENUM class -

    public enum Preference {
        PRIMARY,
        SECONDARY
    }    
    

    I am making a call like this to construct the InputKeys parameter -

    InputKeys keys = new InputKeys.Builder(12000L, 33L).build();    
    System.out.println(keys);
    

    The only problem here I am seeing is, if the customers are not passing any timeout value, I need to have default timeout value set as 500 always but if they are passing any timeout value then it should override my default timeout value. And this is not working for me as when I see my keys in the debug mode, it always has timeout value as 0.

    Is there anything I am missing? And also I am trying to make this class immutable and thread safe.. Is this a thread safe and immutable version of Builder or I have missed something?

    UPDATE:-

    Specific scenarios when people will be using this is we have a factory which customers are going to use to make a call to our code. We have a simple interface which one of our class is implementing it and then we have a factory by which we will call a certain method of that implementation which accepts this keys parameter.

    So they will be using the below code in there application to make a call and it might be possible they will be running there application in a multithreaded environment.

    InputKeys keys = new InputKeys.Builder(12000L, 33L).build();    
    
    IClient client = ClientFactory.getInstance();
    client.getData(keys);
    

    And then InputKeys Builder can be used multiple times to construct the keys, it's not one time. Whatever userId and clientId they will be getting they will construct the InputKeys again.