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

How to make sure my map is never modified once set in my Builder pattern?

下面是我在我的一个项目中使用的构建器模式,我想让它在多线程环境中是线程安全的。一旦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,但有时它会为空。我们并不总是会设置parameterMap。以下是我在我的应用程序代码中设置并传递 ClientKey 对象后在我的应用程序代码中迭代 parameterMap 的方式:

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 对象,parameterMap 只会被读取而不会被修改。然而,它依赖于这样一个事实,即没有恶意或错误的客户端不会同时尝试修改,那么防止这种情况的最佳方法是什么?

如果要在创建 ClientKey 实例后防止对地图 contents 进行 any 修改,您应该在构造函数中使用它:

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

如果您只想允许来自 ClientKey 代码的修改,而不是来自外部的修改,您应该 return 一个 unmodifiableMap() in getParameterMap(),但随后它不会是完全线程安全的——那么你必须使用 ConcurrentMap

在这种情况下,我建议使用 Guava 的 ImmutableMap. I also suggest avoiding null value for parameterMap field and use an empty ImmutableMap instead. For that you could also use its Builder

因此您的代码片段将变为:

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

使用不可变映射将使 ClientKey 用户清楚地知道它不能被修改。