用,真是……优雅?这是明显的“矫枉过正”!一个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 CacheHelper.Get(
delegate(out List<User> data) // cache getter
{
object objData = cacheManager.Get(cacheKey);
data = (objData == null) ? null : (List<User>)objData;
return objData != null;
},
() => // source getter
{
return new UserService().GetFriends(userId);
},
(data) => // cache setter
{
cacheManager.Set(cacheKey, data);
});
}
看上去是不是有点古怪?其实习惯了就好。这种做法有好处还不少:
可读性好:操作的逻辑被分割在不同block中。
编程方便:能够直接使用方法的参数和外部对象,不会有封装的麻烦。
调试方便:设置断点之后可以轻松看出“从缓存中读取”、“从数据源读取”和“写入缓存”的过程。
在出现匿名方法之后,这种将委托作为参数传入方法的做法其实已经非常普遍了。例如在微软推出的并行库中就能使用同样的调用方式:
void ParallelCalculate()
{
double result = 0;
object syncObj = new object();
List<int> list = GetIntList();
Parallel.For<double>(
0,
list.Count,
() => 0,
(index, ps) =>
{
int value = list[index];
for (int n = 0; n < 100; n++)
{
ps.ThreadLocalState += Math.Sqrt(value) * Math.Sin(value);
}
},
(threadResult) =>
{
lock (syncObj)
{
result += threadResult;
}
});
Console.WriteLine("result = " + result);
}
您接受这种做法了吗?