Java 通过在编译时进行验证来改进构建器模式
我最近开始在我的一个项目中使用Builder模式,我正在尝试在我的Builder类上添加某种验证。我假设我们不能在编译时进行验证,所以我在运行时进行验证。但可能是我错了,这就是我想看看我是否能在编译时做到这一点的原因 传统的构建器模式Java 通过在编译时进行验证来改进构建器模式,java,design-patterns,builder,builder-pattern,Java,Design Patterns,Builder,Builder Pattern,我最近开始在我的一个项目中使用Builder模式,我正在尝试在我的Builder类上添加某种验证。我假设我们不能在编译时进行验证,所以我在运行时进行验证。但可能是我错了,这就是我想看看我是否能在编译时做到这一点的原因 传统的构建器模式 public final class RequestKey { private final Long userid; private final String deviceid; private final String flowid;
public final class RequestKey {
private final Long userid;
private final String deviceid;
private final String flowid;
private final int clientid;
private final long timeout;
private final boolean abcFlag;
private final boolean defFlag;
private final Map<String, String> baseMap;
private RequestKey(Builder builder) {
this.userid = builder.userid;
this.deviceid = builder.deviceid;
this.flowid = builder.flowid;
this.clientid = builder.clientid;
this.abcFlag = builder.abcFlag;
this.defFlag = builder.defFlag;
this.baseMap = builder.baseMap.build();
this.timeout = builder.timeout;
}
public static class Builder {
protected final int clientid;
protected Long userid = null;
protected String deviceid = null;
protected String flowid = null;
protected long timeout = 200L;
protected boolean abcFlag = false;
protected boolean defFlag = true;
protected ImmutableMap.Builder<String, String> baseMap = ImmutableMap.builder();
public Builder(int clientid) {
checkArgument(clientid > 0, "clientid must not be negative or zero");
this.clientid = clientid;
}
public Builder setUserId(long userid) {
checkArgument(userid > 0, "userid must not be negative or zero");
this.userid = Long.valueOf(userid);
return this;
}
public Builder setDeviceId(String deviceid) {
checkNotNull(deviceid, "deviceid cannot be null");
checkArgument(deviceid.length() > 0, "deviceid can't be an empty string");
this.deviceid = deviceid;
return this;
}
public Builder setFlowId(String flowid) {
checkNotNull(flowid, "flowid cannot be null");
checkArgument(flowid.length() > 0, "flowid can't be an empty string");
this.flowid = flowid;
return this;
}
public Builder baseMap(Map<String, String> baseMap) {
checkNotNull(baseMap, "baseMap cannot be null");
this.baseMap.putAll(baseMap);
return this;
}
public Builder abcFlag(boolean abcFlag) {
this.abcFlag = abcFlag;
return this;
}
public Builder defFlag(boolean defFlag) {
this.defFlag = defFlag;
return this;
}
public Builder addTimeout(long timeout) {
checkArgument(timeout > 0, "timeout must not be negative or zero");
this.timeout = timeout;
return this;
}
public RequestKey build() {
if (!this.isValid()) {
throw new IllegalStateException("You have to pass at least one"
+ " of the following: userid, flowid or deviceid");
}
return new RequestKey(this);
}
private boolean isValid() {
return !(TestUtils.isEmpty(userid) && TestUtils.isEmpty(flowid) && TestUtils.isEmpty(deviceid));
}
}
// getters here
}
公共最终类请求密钥{
私有最终长用户ID;
专用最终字符串设备ID;
私有最终字符串flowid;
私人最终int客户ID;
私有最终长超时;
私有最终布尔abcFlag;
私有最终布尔标志;
私人最终地图底图;
私有请求密钥(生成器){
this.userid=builder.userid;
this.deviceid=builder.deviceid;
this.flowid=builder.flowid;
this.clientid=builder.clientid;
this.abcFlag=builder.abcFlag;
this.defFlag=builder.defFlag;
this.baseMap=builder.baseMap.build();
this.timeout=builder.timeout;
}
公共静态类生成器{
受保护的最终int客户端ID;
受保护的长userid=null;
受保护的字符串deviceid=null;
受保护的字符串flowid=null;
受保护的长超时=200L;
受保护布尔值abcFlag=false;
受保护的布尔值defFlag=true;
受保护的ImmutableMap.Builder baseMap=ImmutableMap.Builder();
公共生成器(int客户端ID){
checkArgument(clientid>0,“clientid不能为负或零”);
this.clientid=clientid;
}
公共生成器setUserId(长用户id){
checkArgument(userid>0,“userid不能为负或零”);
this.userid=Long.valueOf(userid);
归还这个;
}
公共生成器setDeviceId(字符串deviceid){
checkNotNull(deviceid,“deviceid不能为null”);
checkArgument(deviceid.length()>0,“deviceid不能是空字符串”);
this.deviceid=deviceid;
归还这个;
}
公共生成器setFlowId(字符串flowid){
checkNotNull(flowid,“flowid不能为null”);
checkArgument(flowid.length()>0,“flowid不能是空字符串”);
this.flowid=flowid;
归还这个;
}
公共生成器基本映射(映射基本映射){
checkNotNull(baseMap,“baseMap不能为null”);
this.baseMap.putAll(baseMap);
归还这个;
}
公共生成器abcFlag(布尔abcFlag){
this.abcFlag=abcFlag;
归还这个;
}
公共生成器定义标志(布尔定义标志){
this.defsflag=defsflag;
归还这个;
}
公共生成器addTimeout(长超时){
checkArgument(超时>0,“超时不能为负或零”);
this.timeout=超时;
归还这个;
}
公共请求密钥生成(){
如果(!this.isValid()){
抛出新的非法状态异常(“您必须至少通过一个”
+“以下各项之一:userid、flowid或deviceid”);
}
返回新的RequestKey(这个);
}
私有布尔值isValid(){
return!(TestUtils.isEmpty(userid)和&TestUtils.isEmpty(flowid)和&TestUtils.isEmpty(deviceid));
}
}
//这里有吸气剂
}
问题陈述:
如您所见,我有各种参数,但只有一个参数clientId
是必需的,其余参数是可选的。在我上面的代码中,我需要设置userid
、flowid
或deviceid
。如果这三项都没有设置,那么我将抛出带有错误消息的IllegalStateException
,如代码中所示。如果所有的三个或两个都设置好了,那么就可以了,我正在对这三个或两个做一些优先级逻辑来决定使用哪一个,但至少要设置其中一个
并非强制要求他们每次都通过所有三个id,他们可以通过所有三个,有时两个,有时只有一个,但条件是应设置其中一个
我要寻找的是——我可以在编译时做这些事情,而不是在运行时做这些事情,并且不构建构建器模式,除非这三者中的任何一个被设置,并且在编译时它应该告诉我缺少什么
我发现了这一点,所以它完全谈论了相同的事情,但不确定如何在我的场景中使用它?还有这个和这个根据您的描述,我将使用您已经提到的解决方案: 还有一点变化: 这两种解决方案基本上是一个接一个地链接构建器:调用
builder.create().firstMandatoryField()
,它将返回第二个必填字段的构建器实例,依此类推,直到到达最后一个构建器,最后一个构建器具有调用私有构造函数的实际build
方法
与您的情况一样,有些字段必须至少设置其中一个字段,这意味着您的第一个构建器将提供初始化这些字段的方法,并返回第二个构建器的实例。在第二个生成器上,您可以设置所有字段(可选字段和必填字段)
这是实现这一目标的一个版本:
public final class RequestKey {
private final Long userid;
private final String deviceid;
private final String flowid;
private final int clientid;
private final long timeout;
private final boolean abcFlag;
private final boolean defFlag;
private final Map<String, String> baseMap;
private RequestKey(FinalBuilder builder) {
this.userid = builder.userid;
this.deviceid = builder.deviceid;
this.flowid = builder.flowid;
this.clientid = builder.clientid;
this.abcFlag = builder.abcFlag;
this.defFlag = builder.defFlag;
this.baseMap = builder.baseMap.build();
this.timeout = builder.timeout;
}
public static class Builder {
public Builder1 clientId(int clientid) {
checkArgument(clientid > 0, "clientid must not be negative or zero");
return new Builder1(clientid);
}
}
public static class Builder1 {
private final int clientid;
Builder1(int clientid){
this.clientid = clientid;
}
public FinalBuilder userId(long userid) {
checkArgument(userid > 0, "userid must not be negative or zero");
FinalBuilder builder = new FinalBuilder(clientid);
return builder.setUserId(userid);
}
public FinalBuilder deviceId(String deviceid) {
checkNotNull(deviceid, "deviceid cannot be null");
checkArgument(deviceid.length() > 0, "deviceid can't be an empty string");
FinalBuilder builder = new FinalBuilder(clientid);
return builder.setDeviceId(deviceid);
}
public FinalBuilder flowId(String flowid) {
checkNotNull(flowid, "flowid cannot be null");
checkArgument(flowid.length() > 0, "flowid can't be an empty string");
FinalBuilder builder = new FinalBuilder(clientid);
return builder.setFlowId(flowid);
}
}
public static class FinalBuilder {
private final int clientid;
private Long userid = null;
private String deviceid = null;
private String flowid = null;
private long timeout = 200L;
private boolean abcFlag = false;
private boolean defFlag = true;
private ImmutableMap.Builder<String, String> baseMap = ImmutableMap.builder();
FinalBuilder(int clientId) {
this.clientid = clientId;
}
FinalBuilder setUserId(long userid) {
this.userid = userid;
return this;
}
FinalBuilder setDeviceId(String deviceid) {
this.deviceid = deviceid;
return this;
}
FinalBuilder setFlowId(String flowid) {
this.flowid = flowid;
return this;
}
public FinalBuilder userId(long userid) {
checkArgument(userid > 0, "userid must not be negative or zero");
this.userid = Long.valueOf(userid);
this.userid = userid;
return this;
}
public FinalBuilder deviceId(String deviceid) {
checkNotNull(deviceid, "deviceid cannot be null");
checkArgument(deviceid.length() > 0, "deviceid can't be an empty string");
this.deviceid = deviceid;
return this;
}
public FinalBuilder flowId(String flowid) {
checkNotNull(flowid, "flowid cannot be null");
checkArgument(flowid.length() > 0, "flowid can't be an empty string");
this.flowid = flowid;
return this;
}
public FinalBuilder baseMap(Map<String, String> baseMap) {
checkNotNull(baseMap, "baseMap cannot be null");
this.baseMap.putAll(baseMap);
return this;
}
public FinalBuilder abcFlag(boolean abcFlag) {
this.abcFlag = abcFlag;
return this;
}
public FinalBuilder defFlag(boolean defFlag) {
this.defFlag = defFlag;
return this;
}
public FinalBuilder addTimeout(long timeout) {
checkArgument(timeout > 0, "timeout must not be negative or zero");
this.timeout = timeout;
return this;
}
public RequestKey build() {
return new RequestKey(this);
}
}
public static Builder create() {
return new Builder();
}
// getters here
}
你能举一个例子,在我的例子中它是什么样子的吗?这是我无法理解的。谢谢你的解释。当我尝试您的建议时,它给出了各种字段名的编译错误,因为它无法以某种方式找到这些字段名。事实上,这个模式让我很困惑,这就是为什么我想粘贴它并了解它是如何工作的..修复了编译问题。谢谢修复。有什么方法可以像我现在使用的那样通过m中的构造函数来使用clientId吗
RequestKey.create()
.clientId(1234) // Builder of the first level for the mandatory field
.userId(549375349) // Builder of the second level for any of the additional three mandatory fields
.flowId("flow number") // Builder on the last level allows setting and overriding the three additional mandatory fields
.timeout(3600*1000) // Builder on the last level allows setting of the optional fields
.build(); // Create the instance