2014-03-28 7 views
0

나는 웹 사이트를위한 캐싱 서비스를 만들고 있으며, 캐시 미스를 위해 서비스에서 호출 할 수있는 Func<TKey,TValue> 또는 Func<TKey,Task<TValue>> 메서드를 사용할 수있는 공용 인터페이스를 제공하려고합니다.기다리는 메서드를 호출하는 메서드와 정상 메서드를 호출하는 메서드를 리팩터링하는 방법이 있습니까?

저는이 두 가지 대리자 유형을 처리 할 때 코드 복제가 끝납니다. 통합 할 수있는 방법이 있습니까? 특히 '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; 
    } 
} 

답변

5

첫째, 당신이 당신의 ICacheServiceDataAccessor을 재고해야

여기 내 코드입니다. 키가 캐시에 없을 때 불필요한 값을 계산할 수도 있습니다. 잠시 동안 그 문제를 무시 - -

public interface ICacheServiceDataAccessor 
{ 
    Task<CacheResult<TValue>> TryGetAsync<TValue>(string key); 
    Task<CacheResult<TValue>> GetOrPutAsync<TValue>(string key, Func<Task<TValue>> result); 
} 

그러나 비동기 호출로 동기 호출 치료하는 방법이있다 : I는 다음과 같이 제안 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에서 확장 메소드로 정의 할 수 있습니다.