是否有一种方法可以重构两个方法,一个调用等待方法,另一个调用普通方法
本文关键字:方法 调用 一个 另一个 两个 等待 重构 一种 是否 | 更新日期: 2023-09-27 17:54:34
我正在为一个网站构建一个缓存服务,我想提供一个公共接口,可以采用Func<TKey,TValue>
或Func<TKey,Task<TValue>>
方法a,可以由服务调用缓存miss
在处理这两种委托类型时,我最终重复了代码。有办法合并吗?我特别担心'Func'方法不是线程安全的,并且不适合包装在'Task.Run'中。
下面是我的代码:public interface ICacheServiceEngine
{
Task<CacheResult<TValue>> TryGetValueAsync<TValue>(string key);
Task<CacheResult<TValue>>
TryGetValueAsync<TKey,TValue>(TKey key, Func<TKey,string> keyFunc);
Task<TValue> GetValueAsync<TValue>(string key,
Func<string, TValue> valueSourceFunc);
Task<TValue> GetValueAsync<TKey,TValue>(TKey key,
Func<TKey,string> keyFunc, Func<TKey, TValue> valueSourceFunc);
Task<TValue> GetValueAsync<TValue>(string key,
Func<string, Task<TValue>> valueSourceFuncAsync);
Task<TValue> GetValueAsync<TKey, TValue>(TKey key,
Func<TKey,string> keyFunc, Func<TKey, Task<TValue>> valueSourceFuncAsync);
}
public interface ICacheServiceDataAccessor
{
Task<CacheResult<TValue>> TryGetAsync<TValue>(string key);
Task PutAsync<TValue>(string key , TValue result);
}
public class CacheServiceEngine : ICacheServiceEngine
{
private ICacheServiceDataAccessor cacheDataAccessor;
public CacheServiceEngine(ICacheServiceDataAccessor cacheDataAccessor)
{
// add guard
this.cacheDataAccessor = cacheDataAccessor;
}
public async Task<CacheResult<TValue>> TryGetValueAsync<TValue>(string key)
{
return await this.cacheDataAccessor.TryGetAsync<TValue>(key);
}
public async Task<CacheResult<TValue>> TryGetValueAsync<TKey,TValue>(TKey key,
Func<TKey,string> keyFunc)
{
string keyString = keyFunc(key);
return await this.cacheDataAccessor.TryGetAsync<TValue>(keyString);
}
public async Task<TValue> GetValueAsync<TValue>(string key,
Func<string, TValue> valueSourceFunc)
{
return await this.InnerGetValueAsync(key, () => valueSourceFunc(key));
}
public async Task<TValue> GetValueAsync<TKey,TValue>(TKey key,
Func<TKey,string> keyFunc, Func<TKey, TValue> valueSourceFunc)
{
string keyString = keyFunc(key);
return await this.InnerGetValueAsync(keyString, () => valueSourceFunc(key));
}
public async Task<TValue> GetValueAsync<TValue>(string key,
Func<string, Task<TValue>> valueSourceFuncAsync)
{
return await this.InnerGetValueAsync(key, () => valueSourceFuncAsync(key));
}
public async Task<TValue> GetValueAsync<TKey, TValue>(TKey key,
Func<TKey,string> keyFunc, Func<TKey, Task<TValue>> valueSourceFuncAsync)
{
string keyString = keyFunc(key);
return await this.InnerGetValueAsync(keyString,
() => valueSourceFuncAsync(key));
}
// the two private methods are very close to each other
// can I pull out the similarities, if I assume that 'valueSourceFunc'
// is not thread-safe?
private async Task<TValue> InnerGetValueAsync<TValue>(string key,
Func<TValue> valueSourceFunc)
{
TValue value;
CacheResult<TValue> cacheResult =
await this.cacheDataAccessor.TryGetAsync<TValue>(key);
if (cacheResult.InCache)
{
value = cacheResult.Value;
}
else
{
// this call is normal (synchronous)
value = valueSourceFunc();
await this.cacheDataAccessor.PutAsync(key, value);
}
return value;
}
private async Task<TValue> InnerGetValueAsync<TValue>(string key,
Func<Task<TValue>> valueSourceFuncAsync)
{
TValue value;
CacheResult<TValue> cacheResult =
await this.cacheDataAccessor.TryGetAsync<TValue>(key);
if (cacheResult.InCache)
{
value = cacheResult.Value;
}
else
{
// this call has to be awaited
value = await valueSourceFuncAsync();
await this.cacheDataAccessor.PutAsync(key, value);
}
return value;
}
}
首先,您应该重新考虑您的ICacheServiceDataAccessor
。当键不在缓存中时,完全有可能计算不必要的值。我建议这样写:
public interface ICacheServiceDataAccessor
{
Task<CacheResult<TValue>> TryGetAsync<TValue>(string key);
Task<CacheResult<TValue>> GetOrPutAsync<TValue>(string key, Func<Task<TValue>> result);
}
但是——暂时忽略这个问题——有一种方法可以将同步调用视为异步调用:Task.FromResult
.
public Task<CacheResult<TValue>> TryGetValueAsync<TValue>(string key)
{
return cacheDataAccessor.TryGetAsync<TValue>(key);
}
public Task<CacheResult<TValue>> TryGetValueAsync<TKey,TValue>(TKey key,
Func<TKey, string> keyFunc)
{
string keyString = keyFunc(key);
return cacheDataAccessor.TryGetAsync<TValue>(keyString);
}
public Task<TValue> GetValueAsync<TValue>(string key,
Func<string, TValue> valueSourceFunc)
{
return InnerGetValueAsync(key, () => Task.FromResult(valueSourceFunc(key)));
}
public Task<TValue> GetValueAsync<TKey,TValue>(TKey key,
Func<TKey,string> keyFunc, Func<TKey, TValue> valueSourceFunc)
{
string keyString = keyFunc(key);
return InnerGetValueAsync(keyString, () => Task.FromResult(valueSourceFunc(key)));
}
public Task<TValue> GetValueAsync<TValue>(string key,
Func<string, Task<TValue>> valueSourceFuncAsync)
{
return InnerGetValueAsync(key, () => valueSourceFuncAsync(key));
}
public async Task<TValue> GetValueAsync<TKey, TValue>(TKey key,
Func<TKey,string> keyFunc, Func<TKey, Task<TValue>> valueSourceFuncAsync)
{
string keyString = keyFunc(key);
return InnerGetValueAsync(keyString, () => valueSourceFuncAsync(key));
}
作为最后的设计注意事项,我会考虑只将这些最通用的元素作为ICacheServiceEngine
的实际成员。由于其他方法实际上只是该方法的重载(并且无论派生类如何都将具有相同的实现),因此可以将它们定义为ICacheServiceEngine
上的扩展方法。