我最近开始在一个项目中使用Builder模式,并且尝试在Builder类中添加某种形式的验证。我假设我们不能在编译时执行此操作,所以这就是为什么我在运行时执行此验证的原因。但是可能是我错了,这就是我试图查看我是否可以在编译时执行此操作。

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
}


问题陈述:

在上面的构建器模式中,我只有一个参数必需的clientId,其余的都是可选的,但是我需要设置useridflowiddeviceid。如果这三个都没有设置,那么我将抛出IllegalStateException并显示一条错误消息,如上面的代码所示。我正在运行时执行的检查。我想尽可能在​​编译时进行此检查,除非提供了所有内容,否则不要构建我的模式?

他们不必每次都通过所有这三个id,他们可以一次通过所有三个或两个,有时只能一次通过,但条件是必须设置其中之一。

如何改善构建器模式,以便仅在编译时执行id验证,而不是在运行时进行验证?

我发现这个SO link恰好在谈论同一件事,但不确定如何在我的场景中使用它?还有这个builder pattern with a twist和这个SO question

谁能提供一个示例,如何在我的构建器模式中解决此问题?

最佳答案

仅当设置了必需的属性时,使build方法可访问:

public class Builders {

  /**
   * @param args the command line arguments
   */
  public static void main(String[] args) {

    Builder b = new Builder(123);

//    Builded instance1 = b
//      .defFlag(false)
//      .build(); // compile error

    Builder c1 = b
      .refFlag(true);

    Builded instance2 = b
      .setDeviceId("device id") // here's the magic, without this call `build` method would be unaccessible
      .build();

    Builded instance3 = b
      .refFlag(false)
      .defFlag(true)
      .setDeviceId("device id")
      .setUserId(12)
      .build();

    System.out.printf("%s\n%s\n", instance2, instance3);
  }

}

class Builded implements Cloneable {

  int clientId;

  Long userid;
  String deviceid;
  String flowid;

  boolean defFlag;
  boolean refFlag;

  public Builded(int clientId) {
    this.clientId = clientId;
  }

  @Override
  protected Object clone() throws CloneNotSupportedException {
    return (Builded) super.clone();
  }

  @Override
  public String toString() {
    return String.format("{c: %d, u: %d, d: %s, f: %s, df: %b, rf: %b}", clientId, userid, deviceid, flowid, defFlag, refFlag);
  }

}

class Builder {

  int clientId;
  protected Builded instance;

  private Builder() {
  }

  protected Builder(int clientId) {
    this.clientId = clientId;
    prepare();
  }

  protected final void prepare() {
    instance = new Builded(clientId);
  }

  private Builded build() {
    try {
      return (Builded) instance.clone();
    } catch (CloneNotSupportedException ex) {
      throw new RuntimeException(ex);
    }
  }

  public Builder defFlag(boolean defFlag) {
    instance.defFlag = defFlag;
    return this;
  }

  public Builder refFlag(boolean refFlag) {
    instance.refFlag = refFlag;
    return this;
  }

  public SubBuilder setUserId(long userid) {
    instance.userid = userid;
    return new SubBuilder(instance);
  }

  public SubBuilder setDeviceId(String deviceid) {
    instance.deviceid = deviceid;
    return new SubBuilder(instance);
  }

  public SubBuilder setFlowId(String flowid) {
    instance.flowid = flowid;
    return new SubBuilder(instance);
  }

  public static class SubBuilder extends Builder {

    private SubBuilder(Builded instance) {
      this.instance = instance;
    }

    public Builded build() {
      return super.build();
    }

  }

}


输出:

{c: 123, u: null, d: device id, f: null, df: false, rf: true}
{c: 123, u: 12, d: device id, f: null, df: true, rf: false}

关于java - 改进验证检查时的构建器模式?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34468554/

10-16 20:50