java - 改进验证检查的构建器模式?

标签 java design-patterns builder-pattern

我最近开始在我的一个项目中使用生成器模式,并且我正在尝试在我的生成器类上添加某种验证。我假设我们无法在编译时执行此操作,因此这就是我在运行时执行此验证的原因。但也许我错了,这就是我试图看看是否可以在编译时做到这一点。

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,他们可以传递所有三个,有时两个或有时仅一个,但条件是应设置其中之一。 p>

如何改进构建器模式,以便仅在编译时进行 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/

相关文章:

c++ - 如何在另一个线程中执行一个方法?

c# - 装饰器模式和 C#

c# - 在这种情况下使用什么设计模式?

java - 如何使用构建器模式构建各种相似的对象类型?

java - 使用 C/C++ 的 ptrace Java 程序

java - 从多个供应商生成一个流

java - 在构建器模式中使用抽象类?

java - 我的构建器模式有什么问题?

java - 用于在 char 'x' 之前选择最多 char before char 'y' 的正则表达式

java - 开关语句 : Execute Code Once