我有一个构建器模式,其中我从客户那里获取了一些参数,并基于我构建我的构建器类,然后将该构建器类传递给我们的底层库,然后我的库将使用它。
public final class KeyHolder {
private final String clientId;
private final String deviceId;
private final int processId;
private final Cache<String, List<Response>> userCache;
private static final long MAXIMUM_CACHE_SIZE = 5000000;
private static final long EXPIRE_AFTER_WRITE = 120; // this is in seconds
private KeyHolder(Builder builder) {
this.clientId = builder.clientId;
this.deviceId = builder.deviceId;
this.processId = builder.processId;
this.maximumCacheSize = builder.maximumCacheSize;
this.expireAfterWrite = builder.expireAfterWrite;
// how to execute this line only once
this.userCache =
CacheBuilder
.newBuilder()
.maximumSize(maximumCacheSize)
.expireAfterWrite(expireAfterWrite, TimeUnit.SECONDS)
.removalListener(
RemovalListeners.asynchronous(new CustomListener(),
Executors.newSingleThreadScheduledExecutor())).build();
}
public static class Builder {
protected final int processId;
protected String clientId = null;
protected String deviceId = null;
protected long maximumCacheSize = MAXIMUM_CACHE_SIZE;
protected long expireAfterWrite = EXPIRE_AFTER_WRITE;
public Builder(int processId) {
this.processId = processId;
}
public Builder setClientId(String clientId) {
this.clientId = clientId;
return this;
}
public Builder setDeviceId(String deviceId) {
this.deviceId = deviceId;
return this;
}
public Builder setMaximumCacheSize(long size) {
this.maximumCacheSize = size;
return this;
}
public Builder setExpiryTimeAfterWrite(long duration) {
this.expireAfterWrite = duration;
return this;
}
public KeyHolder build() {
return new KeyHolder(this);
}
}
// getters here
}
对于我们库的每次调用,他们每次都会创建一个新的KeyHolder
构建器类并将其传递给我们的库。 processId
,clientId
,deviceId
会随着每次通话而改变,但maximumCacheSize
和expireAfterWrite
将与每次通话保持一致。正如您在上面所看到的,我在这里使用guava缓存,因为他们每次都在创建KeyHolder
构建器类,如何确保下面的行只在构造函数中执行一次?
this.userCache =
CacheBuilder
.newBuilder()
.maximumSize(maximumCacheSize)
.expireAfterWrite(expireAfterWrite, TimeUnit.SECONDS)
.removalListener(
RemovalListeners.asynchronous(new CustomListener(),
Executors.newSingleThreadScheduledExecutor())).build();
由于现在使用当前代码,它将在每次调用时执行,我每次都会在我的库中获得一个新的guava缓存,因此使用此guava缓存在我的库中先前缓存的任何条目都将丢失。< / p>
如何仅将一个特定变量初始化一次,之后它应该忽略传递给它的值?
更新
public class DataClient implements Client {
private final ExecutorService executor = Executors.newFixedThreadPool(10);
// for synchronous call
@Override
public List<Response> executeSync(KeyHolder key) {
Cache<String, List<Response>> userCache = key.getUserCache();
List<Response> response = userCache.getIfPresent(key.getUUID());
if (CollectionUtils.isNotEmpty(response)) {
return response;
}
// if not in cache, then normally call the flow and populate the cache
List<Response> dataResponse = null;
Future<List<Response>> future = null;
try {
future = executeAsync(key);
dataResponse = future.get(key.getTimeout(), TimeUnit.MILLISECONDS);
userCache.put(key.getUUID(), dataResponse);
} catch (TimeoutException ex) {
// log error and return DataResponse
} catch (Exception ex) {
// log error and return DataResponse
}
return dataResponse;
}
}
答案 0 :(得分:1)
如果您只想设置一次缓存,为什么每个KeuHolder
对象都会尝试构建它?事实上,即使KeyHolder#Builder
公开了方法来帮助构建缓存,这只会有用一次。
这非常值得怀疑。如果第一个KeyHolder
未指定缓存详细信息,该怎么办?我的意思是,它没有被强迫(你没有正确使用构建器模式,最后更多)。
解决此问题的第一步是确保在开始创建KeyHolder
对象之前设置缓存。您可以通过创建静态工厂并使userCache
静态:
class KeyHolder {
private static Map<String, List<Response>> userCache;
public static KeyHolder.Builder newBuilder(int id) {
if(userCache == null) {
userCache = ...;
}
return new Builder(id);
}
}
但是你可能已经从我的评论中读到,这只是这个问题的创可贴。每当我们想要创建一个新的KeyHolder
时,这将检查userCache,这不应该发生。
相反,您应该将缓存与KeyHolder
一起解耦。为什么还需要了解缓存?
您的缓存属于DataClient
:
class DataClient {
private Map<String, List<Response>> userCache;
public List<Response> executeSync(KeyHolder key) {
List<Response> response = userCache.getIfPresent(key.getUUID());
//...
}
}
您可以通过DataClient
构造函数接受设置,或使用已指定的设置将缓存传递到DataClient
。
至于你对构建器模式的使用,请记住我们使用它的原因:Java缺少可选参数。
这就是构建器很常见的原因:它们允许我们通过方法指定可选数据。
您将关键信息(例如缓存设置)指定为可选参数(构建器方法)。如果您不需要信息,则应该只使用构建器方法,并且缓存信息肯定是必需的。我会询问可选的deviceId
和clientId
是如何进行的,看看唯一需要的数据是productId
。
答案 1 :(得分:0)
private static Cache<String, List<Response>> userCache;
然后,只有在尚未初始化时才初始化
private KeyHolder(Builder builder) {
...
if (userCache == null) {
userCache = CacheBuilder
.newBuilder()
.maximumSize(maximumCacheSize)
.expireAfterWrite(expireAfterWrite, TimeUnit.SECONDS)
.removalListener(
RemovalListeners.asynchronous(new CustomListener(), Executors.newSingleThreadScheduledExecutor())
).build();
}
}
如果你真的不需要在运行时自定义缓存(通过使用传入的参数,我建议使用类似
的内容)// initialize the cache while defining it
// replace maximumCacheSize and expireAfterWrite with constants
private static final Cache... = CacheBuilder.newBuilder()...;
并将其从构造函数中删除。
答案 2 :(得分:0)
您可以将变量设为静态,只在第一次调用时初始化它,如果它为null。像这样:
public final class KeyHolder {
private final String clientId;
private final String deviceId;
private final int processId;
//this var is now static, so it is shared across all instances
private static Cache<String, List<Response>> userCache = null;
private static final long MAXIMUM_CACHE_SIZE = 5000000;
private static final long EXPIRE_AFTER_WRITE = 120; // this is in seconds
private KeyHolder(Builder builder) {
this.clientId = builder.clientId;
this.deviceId = builder.deviceId;
this.processId = builder.processId;
this.maximumCacheSize = builder.maximumCacheSize;
this.expireAfterWrite = builder.expireAfterWrite;
//this will be executed only the first time, when the var is null
if (userCache == null) {
userCache =
CacheBuilder
.newBuilder()
.maximumSize(maximumCacheSize)
.expireAfterWrite(expireAfterWrite, TimeUnit.SECONDS)
.removalListener(
RemovalListeners.asynchronous(new CustomListener(),
Executors.newSingleThreadScheduledExecutor())).build();
}
//rest of your class below