下面是我在我的一个项目中使用的构建器模式,我想在多线程环境中使其成为线程安全的。设置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并且永远不会修改它。然而,它依赖于这样一个事实,即在此期间没有恶意或有错误的客户端没有尝试修改,那么防止这种情况的最佳方法是什么?
答案 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.