问题
I am trying to use Builder Pattern for my below class.. Initially I was using constructor of my class to set all the parameters but accidentally I came across Builder pattern and it is looking good for my use case.
Below is my class in which people will mostly pass userId
, clientId
and parameterMap
always but other fields are optional and they may or may not pass it. And also if they 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. Here Preference is an ENUM having four fields.
public final class ModelInput {
private long userid;
private long clientid;
private long timeout = 500L;
private Preference pref;
private boolean debug;
private Map<String, String> parameterMap;
public ModelInput(long userid, long clientid, Preference pref, Map<String, String> parameterMap, long timeout, boolean debug) {
this.userid = userid;
this.clientid = clientid;
this.pref = pref;
this.parameterMap = parameterMap;
this.timeout = timeout;
this.debug = debug;
}
... //getters here
}
Below is an example how I was using initially to construct ModelInput
object by passing parameters to constructor. Initially I was passing all the parameters but clients will mostly pass userId
, clientId
and parameterMap
always and other fields are optional..
Map<String, String> paramMap = new HashMap<String, String>();
paramMap.put("attribute", "segmentation");
ModelInput input = new ModelInput(109739281L, 20L, Preference.SECONDARY, paramMap, 1000L, true);
How do I convert my above code to start using Builder pattern as told by Bloch's in Effective Java which is also thread safe and immutable as well?
And how can I do the validation check as well on this using Builder pattern? It might be possible people can pass userId zero or negative numbers same with client id and timeout and same with map as well..
回答1:
Builder constructor must have the mandatory parameters. So, in your case, if userId, clientId and parameterMap are mandatory, we will have something like that:
public final class ModelInput {
private long userid;
private long clientid;
private long timeout = 500L;
private Preference pref;
private boolean debug;
private Map<String, String> parameterMap;
public ModelInput(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 {
private long userId;
private long clientId;
private Preference preference;
private boolean debug;
private Map<String, String> parameterMap;
public Builder(long userId, long clientId, Map<String, String> parameterMap) {
this.userId = userId;
this.clientId = clientId;
this.parameterMap = parameterMap;
}
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 ModelInput build() {
return ModelInput(this);
}
}
// ModelInput getters / setters
}
And this is how to use your builder class:
String paramMap = new HashMap<String, String>();
paramMap.put("attribute", "segmentation");
ModelInput.Builder builder = new ModelInput.Builder(109739281L, 20L, paramMap);
builder.preference(Preference.SECONDARY).timeout(1000L).debug(true);
ModelInput modelInput = builder.build();
Hope this helps :)
回答2:
I am not familiar with the book you are referencing, but I would suggest reading the post of wikipedia regarding the Builder pattern which is rather straight forward.
An implementation could be:
public final class ModelInput {
...
public static class Builder {
private long userid;
...
public Builder(long userid) {
this.userid = userid;
}
...
public ModelInput build() {
return new ModelInput(this);
}
}
private ModelInput(Builder builder){
this.userid = builder.userid;
this.clientid = builder.clientid;
this.pref = builder.pref;
this.parameterMap = builder.parameterMap;
this.timeout = builder.timeout;
this.debug = builder.debug;
}
...
}
Then when you want to initialize an object, you would call
ModelInput model = new ModelInput.Builder(...).build();
Regarding the validation process, it would be essentially the same as it would be made by checking the values (either in the constructor, or in the build
method).
Hope I helped!
回答3:
try this
public final class ModelInput {
...
public static class Builder {
private long userid;
...
public Builder setUserId(long userId) {
this.userId = userId;
}
...
public ModelInput build() {
return new ModelInput(userId,...
}
}
}
回答4:
To add to what the others have written:
In order to make your class immutable, you need to make parameterMap variable immutable as well. You can do that when the ModelInput class is instantiated:
private ModelInput(Builder builder){
this.userid = builder.userid;
this.clientid = builder.clientid;
this.pref = builder.pref;
this.parameterMap = Collections.unmodifiableMap(builder.parameterMap);
this.timeout = builder.timeout;
this.debug = builder.debug;
}
Notice the use of Collections.unmodifiablemap();
You'll also need to make your Preference instance immutable, or at least make it known that it is in fact not immutable.
来源:https://stackoverflow.com/questions/21040771/how-to-use-builder-pattern-as-described-by-joshua-blochs-version-in-my-modelinp