如何在我的Builder模式中设置后确保我的地图永远不会被修改?

时间:2015-05-03 18:26:02

标签: java multithreading dictionary thread-safety final

下面是我在我的一个项目中使用的构建器模式,我想在多线程环境中使其成为线程安全的。设置ClientKey后,我不希望任何人再次修改它。

public final class ClientKey {

    private final long userId;
    private final int clientId;
    private final long timeout;
    private final boolean dataFlag;
    // how can I make sure that my parameterMap is never modified once set
    private final Map<String, String> parameterMap;

    private ClientKey(Builder builder) {
        this.userId = builder.userId;
        this.clientId = builder.clientId;
        this.remoteFlag = builder.remoteFlag;
        this.dataFlag = builder.dataFlag;
        this.parameterMap = builder.parameterMap;
        this.timeout = builder.timeout;
    }

    public static class Builder {
        protected final long userId;
        protected final int clientId;
        protected long timeout = 200L;
        protected boolean remoteFlag = false;
        protected boolean dataFlag = true;
        protected Map<String, String> parameterMap;

        public Builder(long userId, int clientId) {
            this.userId = userId;
            this.clientId = clientId;
        }

        public Builder parameterMap(Map<String, String> parameterMap) {
            this.parameterMap = parameterMap;
            return this;
        }

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

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

        public Builder addTimeout(long timeout) {
            this.timeout = timeout;
            return this;
        }

        public ClientKey build() {
            return new ClientKey(this);
        }
    }

    public long getUserId() {
        return userId;
    }

    public int getClientId() {
        return clientId;
    }

    public long getTimeout() {
        return timeout;
    }

    public Map<String, String> getParameterMap() {
        return parameterMap;
    }

    public boolean isDataFlag() {
        return dataFlag;
    }
}

在上面的ClientKey中,有时我会在创建ClientKey对象时传递parameterMap,但有时候,它会为null。我们并不总是设置parameterMap。以下是我在应用程序代码中迭代parameterMap的方式,一旦设置ClientKey对象并在我的应用程序代码中传递:

final Map<String, String> parameterMap = clientKey.getParameterMap();
if (!MapUtils.isEmpty(parameterMap)) {
    Set<Entry<String, String>> params = parameterMap.entrySet();

    for (Entry<String, String> e : params) {
        url.append("&").append(e.getKey());
        url.append("=").append(e.getValue());
    }
}

问题是 - 我怎样才能确保我的parameterMap在设置之后永远不会被修改。正如您在构建ClientKey对象时所看到的那样,只能读取参数Map并且永远不会修改它。然而,它依赖于这样一个事实,即在此期间没有恶意或有错误的客户端没有尝试修改,那么防止这种情况的最佳方法是什么?

2 个答案:

答案 0 :(得分:2)

如果要在创建ClientKey实例后阻止对地图内容进行任何修改,则应在构造函数中使用它:

    if (builder.parameterMap == null) {
        this.parameterMap = null;
    } else {
        this.parameterMap = Collections.unmodifiableMap(builder.parameterMap);
    }

如果您只想从ClientKey代码进行修改,而不是从外部进行修改,则应该在unmodifiableMap()中返回getParameterMap(),但之后它不会完全线程安全 - 您必须使用ConcurrentMap然后。

答案 1 :(得分:1)

In such cases I recommend using Guava's ImmutableMap. I also suggest avoiding null value for parameterMap field and use an empty ImmutableMap instead. For that you could also use its Builder.

So your code fragment would become:

public class ClientKey {
   ...
   private final ImmutableMap<String, String> parameterMap;

   public ClientKey(Builder builder) {
       ...
       this.parameterMap = builder.parameterMap.build();
       ...
   }

   public static class Builder {
        ...
        protected ImmutableMap.Builder<String, String> parameterMap = ImmutableMap.builder();

        public Builder parameterMap(Map<String, String> parameterMap) {
            this.parameterMap.putAll(parameterMap);
            return this;
        }

        public Builder addParameter(String key, String value) {
            this.parameterMap.put(key, value);
            return this;
        }
     }
 }

Using immutable map will make it clear to the ClientKey users that it cannot be modified.