1

我正在尝试为下面的类使用 Builder 模式。最初我使用类的构造函数来设置所有参数,但不小心遇到了 Builder 模式,它看起来很适合我的用例。

下面是我的课程,大多数人都会通过userIdclientId并且parameterMap总是但其他字段是可选的,他们可能会通过也可能不会通过。而且,如果他们没有传递任何超时值,我需要始终将默认超时值设置为 500,但如果他们传递任何超时值,那么它应该覆盖我的默认超时值。这里的 Preference 是一个有四个字段的 ENUM。

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
}    

下面是一个示例,我最初是如何ModelInput通过将参数传递给构造函数来构造对象的。最初我传递了所有参数,但客户端大多会传递userIdclientId并且parameterMap总是和其他字段是可选的..

Map<String, String> paramMap = new HashMap<String, String>();
paramMap.put("attribute", "segmentation");

ModelInput input = new ModelInput(109739281L, 20L, Preference.SECONDARY, paramMap, 1000L, true);

如何转换上面的代码以开始使用 Bloch 在 Effective Java 中所说的 Builder 模式,它也是线程安全且不可变的?

以及如何使用 Builder 模式对此进行验证检查?人们可能会传递与客户端 ID 和超时相同的 userId 零或负数,也可能与 map 相同..

4

4 回答 4

4

Builder 构造函数必须具有强制参数。因此,在您的情况下,如果 userId、clientId 和 parameterMap 是必需的,我们将有类似的内容:

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
}

这就是如何使用您的构建器类:

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();

希望这可以帮助 :)

于 2014-01-10T10:29:59.293 回答
2

我不熟悉您所引用的书,但我建议阅读维基百科上关于 Builder 模式的帖子,这是相当直截了当的。

一个实现可以是:

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;
    }

...
}

然后当你想初始化一个对象时,你会调用

ModelInput model = new ModelInput.Builder(...).build();

关于验证过程,它与通过检查值(在构造函数中或在build方法中)进行的验证过程基本相同。

希望我有所帮助!

于 2014-01-10T10:10:44.590 回答
0

试试这个

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,...
        }
    }
}  
于 2014-01-10T10:10:40.010 回答
0

添加到其他人所写的内容:

为了使您的类不可变,您还需要使 parameterMap 变量不可变。您可以在实例化 ModelInput 类时执行此操作:

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;
}

注意Collections.unmodifiablemap()的使用;

您还需要使您的 Preference 实例不可变,或者至少让它知道它实际上不是不可变的。

于 2014-01-10T17:51:33.833 回答