I recently started using Builder pattern in one of my projects and I am trying to add some sort of validations on my Builder class. I have already asked a question, got some feedback and incorporated it into my code.
Below is my builder class -
public final class DataKey {
private final long userId;
private final String uuid;
private final String deviceId;
private final int clientId;
private final FlowEnum flow;
private DataKey(Builder builder) {
this.userId = builder.userId;
this.uuid = builder.uuid;
this.deviceId = builder.deviceId;
this.clientId = builder.clientId;
this.flow = (userId == 0) ? FlowEnum.DEVICEFLOW : FlowEnum.USERFLOW;
}
public static class Builder {
protected final int clientId;
protected long userId;
protected String uuid;
protected String deviceId;
protected boolean userIdFlag = false;
protected boolean uuidFlag = false;
protected boolean deviceIdFlag = false;
public Builder(int clientId) {
this.clientId = clientId;
}
public Builder setUserId(long userId) {
this.userId = userId;
this.userIdFlag = true;
return this;
}
public Builder setUuid(String uuid) {
this.uuid = uuid;
this.uuidFlag = true;
return this;
}
public Builder setDeviceId(String deviceId) {
this.deviceId = deviceId;
this.deviceIdFlag = true;
return this;
}
public DataKey build(){
if (userIdFlag) {
if (userId <= 0) {
throw new IllegalArgumentException(" USERID cannot be negative or zero");
}
} else if (uuidFlag && !userIdFlag) {
if (uuid == null || uuid.isEmpty()) {
throw new IllegalArgumentException("UUID cannot be null OR empty");
}
} else if (deviceIdFlag && !userIdFlag && !uuidFlag) {
if (deviceId == null || deviceId.isEmpty()) {
throw new IllegalArgumentException("DEVICEID cannot be null OR empty");
}
} else if (!userIdFlag && !uuidFlag && !deviceIdFlag) {
throw new IllegalStateException("You have to pass at least one"
+ " of the following: userId, cguid or udid");
}
return new DataKey(this);
}
}
public long getUserId() {
return userId;
}
public String getUuid() {
return uuid;
}
public String getDeviceId() {
return deviceId;
}
public FlowEnum getFlow() {
return flow;
}
public int getClientId() {
return clientId;
}
}
I also have an enum which determines whether we are using userId
or uuid
/deviceId
. If userId
is present then flow type is USERFLOW
, otherwise flow type is DEVICEFLOW
.
public enum FlowEnum {
USERFLOW, DEVICEFLOW
}
While making keys using the above Builder class - I will pass clientId
always, and I will pass atleast one "ID key" from these - userId
, uuid
and deviceId
. If by any chance, all the ID keys are missing then I need to throw an illegal state exception.
Problem Statement:
As I mentioned above I have three kind of "ID's" - userId, uuid and deviceId
. userId
takes the priority first, then uuid
and then deviceId
. Now I need to do validations on all these three ID's with below rules -
- Suppose if we are passing all the three ID's, then I need to use
userId
sinceuserId
takes the priority so I will check whetheruserId
is negative or zero, if it is, then throw anIllegalArgumentException
thatuserId
cannot be negative or zero. It is fine, if somebody is passinguuid
anddeviceid
as null string or empty string in this case. - Now suppose if we are passing
uuid
anddeviceid
only, then I need to useuuid
sinceuuid
takes the priority now asuserId
is not present so I will check whetheruuid
is null or empty, if it is, then throw anIllegalArgumentException
thatuuid
cannot be null or empty. It is fine, if somebody is passingdeviceid
as null string or empty string in this case. - Now suppose if we are passing
deviceid
only, then I need to usedeviceid
sincedeviceid
takes the priority now asuserId
anduuid
are not present so I will check whetherdeviceid
is null or empty, if it is, then throw anIllegalArgumentException
thatdeviceid
cannot be null or empty.
I'm opting for code review to see whether we can simplify or improve the above code basis on the above logic if possible. Is there any improvements which I can do? Do I need boolean flag to make this work?
I can wrap null/empty string and long check in a method in my Utilities class but apart from that, is there anything I can improve?
This is one way of making the keys -
DataKey keys = new DataKey.Builder(100).setUserId(1234L).setUuid(null).setDeviceId("").build();
or
DataKey keys = new DataKey.Builder(100).setUserId(1234L).build();
or
DataKey keys = new DataKey.Builder(100).setUserId(1234L).setUuid("asdfg").setDeviceId("htyui").build();
Update:-
There is one issue with your suggestion -
Suppose if I am making keys like this, then it will throw IllegalArgumentException, right? Since uuid and deviceid are null or empty but with my above rules, if userId is set and it is set with proper value, then it should not throw IllegalArgumentException. Meaning if userId is set, then I will validate userId to see whether it is <= 0
or not.
DataKey keys = new DataKey.Builder(100).setUserId(1234L).setUuid("").setDeviceId(null).build();
There is always a priority, userId takes the priority first, then uuid takes the priority and then deviceId takes the priority.
If userId is set, then I will validate userId, but if userId is not set and uuid is set, then I will validate uuid but if userId and uuid is not set, then I will validate deviceId.