嘗試從緩存中獲取數據如果數據存在則返回否則從數據源中獲取數據放入緩存然後返回
您是否熟悉上面這段邏輯說明?如果您的應用中大量使用了緩存則上面這段邏輯很可能會出現許多次例如
CacheManager cacheManager = new CacheManager();
public List<User> GetFriends(int userId)
{
string cacheKey = friends_of_user_ + userId;
object objResult = cacheManagerGet(cacheKey);
if (objResult != null) return (List<User>)objResult;
List<User> result = new UserService()GetFriends(userId);
cacheManagerSet(cacheKey result);
return result;
}
這段邏輯似乎比較簡單不過在實際應用中從數據源中獲取數據可能不是簡單地調用一個方法而是需要多個類之間的協作事務控制等等而緩存的讀寫可能也會比上面的示例來的復雜因此一個可讀性高的做法是提供三個獨立的方法(讀取緩存讀取數據源寫入緩存)使得一個擁有緩存的方法只需要簡單地實現上面所提到的讀寫邏輯即可
正如文章開頭所說如果您的應用中大量使用了緩存則上面這段邏輯很可能會出現許多次在一定程度上這種重復也是多余的違背了DRY原則因此我們設法提供一個基類把這段緩存讀寫邏輯封裝起來
public abstract class CacheReader<T>
{
/// <summary>從緩存中獲取數據</summary>
/// <param name=data>從緩存中取得的數據</param>
/// <returns>從緩存中成功取得數據則返回true反之則false</returns>
public abstract bool GetFromCache(out T data);
/// <summary>從數據源獲取數據</summary>
/// <returns>從數據源取得的對象</returns>
public abstract T ReadFromSource();
/// <summary>將數據寫入緩存</summary>
/// <param name=data>將要寫入緩存的數據</param>
public abstract void SetToCache(T data);
public T Read()
{
T data;
if (thisGetFromCache(out data)) return data;
data = thisReadFromSource();
thisSetToCache(data);
return data;
}
}
於是我們將這段緩存讀寫邏輯集中到了CacheReader類的Read方法中
而對於每個緩存讀寫操作我們只要實現一個CacheReader類的子類提供三個抽象方法的具體實現即可如下
private class GetFriendCacheReader : CacheReader<List<User>>
{
private int m_userId;
private string m_cacheKey;
private CacheManager m_cacheManager;
public GetFriendCacheReader(int userId CacheManager cacheManager)
{
thism_userId = userId;
thism_cacheKey = friends_of_user_ + userId;
thism_cacheManager = cacheManager;
}
public override bool GetFromCache(out List<User> data)
{
object objData = thism_cacheManagerGet(thism_cacheKey);
if (objData == null)
{
data = null;
return false;
}
data = (List<User>)objData;
return true;
}
public override List<User> ReadFromSource()
{
return new UserService()GetFriends(thism_userId);
}
public override void SetToCache(List<User> data)
{
thism_cacheManagerSet(thism_cacheKey data);
}
}
於是我們的GetFriends方法就可以修改成如下模樣
public List<User> GetFriends(int userId)
{
return new GetFriendCacheReader(userId cacheManager)Read();
}
典型的模板方法(Template Method)模式的應用真是……優雅?這是明顯的矯枉過正!一個GetFriends方法需要開發一個類而應用中少說也該有幾十上百個這樣的方法吧?於是乎我們吭哧吭哧地開發了幾十上百個CacheReader的子類每個子類需要把所有用到的對象和數據封裝進去並且實現三個抽象方法——OMG真可謂OOP到了極致!
還好我們可以使用匿名方法為此我們寫一個Helper方法
public static class CacheHelper
{
public delegate bool CacheGetter<TData>(out TData data);
public static TData Get<TData>(
CacheGetter<TData> cacheGetter
Func<TData> sourceGetter
Action<TData> cacheSetter)
{
TData data;
if (cacheGetter(out data))
{
return data;
}
data = sourceGetter();
cacheSetter(data);
return data;
}
}
委托是個好東西可以作為方法的參數使用而匿名方法的存在讓這種方式變得尤其有用例如我們現在就可以
public List<User> GetFriends(int userId)
{
string cacheKey = friends_of_user_ + userId;
return CacheHelperGet(
delegate(out List<User> data) // cache getter
{
object objData = cacheManagerGet(cacheKey);
data = (objData == null) ? null : (List<User>)objData;
return objData != null;
}
() => // source getter
{
return new UserService()GetFriends(userId);
}
(data) => // cache setter
{
cacheManagerSet(cacheKey data);
});
}
看上去是不是有點古怪?其實習慣了就好這種做法有好處還不少
◆可讀性好操作的邏輯被分割在不同block中
◆編程方便能夠直接使用方法的參數和外部對象不會有封裝的麻煩
◆調試方便設置斷點之後可以輕松看出從緩存中讀取
從數據源讀取和寫入緩存的過程
在出現匿名方法之後這種將委托作為參數傳入方法的做法其實已經非常普遍了例如在微軟推出的並行庫中就能使用同樣的調用方式
void ParallelCalculate()
{
double result = ;
object syncObj = new object();
List<int> list = GetIntList();
ParallelFor<double>(
listCount
() =>
(index ps) =>
{
int value = list[index];
for (int n = ; n < ; n++)
{
psThreadLocalState += MathSqrt(value) * MathSin(value);
}
}
(threadResult) =>
{
lock (syncObj)
{
result += threadResult;
}
});
ConsoleWriteLine(result = + result);
}
您接受這種做法了嗎?
您善於使用匿名函數嗎?
From:http://tw.wingwit.com/Article/program/net/201311/12243.html