jusfr 原創(chuàng),轉(zhuǎn)載請(qǐng)注明來(lái)自博客園。
在之前的實(shí)現(xiàn)中,我們初步實(shí)現(xiàn)了一個(gè)緩存模塊:包含一個(gè)基于Http請(qǐng)求的緩存實(shí)現(xiàn),一個(gè)基于HttPRuntime.Cache進(jìn)程級(jí)的緩存實(shí)現(xiàn),但觀察代碼,會(huì)發(fā)現(xiàn)如下問(wèn)題:
1. 有部分邏輯如 Boolean TryGet<T>(String key, out T entry) 的實(shí)現(xiàn)有重復(fù)現(xiàn)象,Do not repeat yourself 提醒我們這里可以改進(jìn);2. 分區(qū)特性雖然實(shí)現(xiàn)了,但是使用了額外的接口承載,而大多數(shù)運(yùn)用中,調(diào)用者無(wú)論是操作緩存項(xiàng)的創(chuàng)建還是過(guò)期,都不太關(guān)心分區(qū)參數(shù) Region;的機(jī)制問(wèn)題,計(jì)數(shù)和全部過(guò)期貌似不太現(xiàn)實(shí),從這個(gè)接口派生恐怕不妥,怎么辦?3. IHttpRuntimeCacheProvider 接口中功能太多,本文要添加一個(gè)基于 Memcached 的緩存實(shí)現(xiàn)類,而 Memcached 天然不支持遍歷等操作怎么辦?
處理第1個(gè)問(wèn)題,先梳理一下緩存獲取即 GetOrCreate 邏輯,多數(shù)情況是這樣的
1)嘗試從某容器或客戶端如 HttpContext.Current.Items、HttpRuntime.Cache、MemcachedClient 判斷緩存是否存在及獲取緩存對(duì)象;2)緩存對(duì)象存在時(shí)進(jìn)行類型對(duì)比,比如 id 已經(jīng)被緩存成整型,現(xiàn)在新接口嘗試將 Guid 類型寫(xiě)入,本文使用嚴(yán)格策略,該操作將拋出 InvalidOperationException 異常;3)緩存不存在時(shí),執(zhí)行委托計(jì)算出緩存值,將其寫(xiě)入容器;
可以看出, GetOrCreate 將調(diào)用 TryGet 方法及 Overwrite 方法,我們可以使用抽象類,將前者寫(xiě)成具體實(shí)現(xiàn),將后兩者寫(xiě)成抽象方法,由具體子類去實(shí)現(xiàn)。
1 public interface ICacheProvider { 2 Boolean TryGet<T>(String key, out T entry); 3 T GetOrCreate<T>(String key, Func<T> function); 4 T GetOrCreate<T>(String key, Func<String, T> factory); 5 void Overwrite<T>(String key, T entry); 6 void Expire(String key); 7 } 8 9 public abstract class CacheProvider : ICacheProvider {10 protected virtual String BuildCacheKey(String key) {11 return key;12 }13 14 protected abstract Boolean InnerTryGet(String key, out Object entry);15 16 public virtual Boolean TryGet<T>(String key, out T entry) {17 String cacheKey = BuildCacheKey(key);18 Object cacheEntry;19 Boolean exist = InnerTryGet(cacheKey, out cacheEntry);20 if (exist) {21 if (cacheEntry != null) {22 if (!(cacheEntry is T)) {23 throw new InvalidOperationException(String.Format("緩存項(xiàng)`[{0}]`類型錯(cuò)誤, {1} or {2} ?",24 key, cacheEntry.GetType().FullName, typeof(T).FullName));25 }26 entry = (T)cacheEntry; 27 }28 else {29 entry = (T)((Object)null);30 }31 }32 else {33 entry = default(T);34 }35 return exist;36 }37 38 public virtual T GetOrCreate<T>(String key, Func<T> function) {39 T entry;40 if (TryGet(key, out entry)) {41 return entry;42 }43 entry = function();44 Overwrite(key, entry);45 return entry;46 }47 48 public virtual T GetOrCreate<T>(String key, Func<String, T> factory) {49 T entry;50 if (TryGet(key, out entry)) {51 return entry;52 }53 entry = factory(key);54 Overwrite(key, entry);55 return entry;56 }57 58 public abstract void Overwrite<T>(String key, T value);59 60 public abstract void Expire(String key);61 }抽象類 CacheProvider 的 InnerTryGet、Overwrite、Expire 是需要實(shí)現(xiàn)類來(lái)完成的,GetOrCreate 調(diào)用它們來(lái)完成核心邏輯;于是HttpContextCacheProvider 的實(shí)現(xiàn),邏輯在父類實(shí)現(xiàn)后,看起來(lái)非常簡(jiǎn)潔了:
1 public class HttpContextCacheProvider : CacheProvider, ICacheProvider { 2 private const String _prefix = "HttpContextCacheProvider_"; 3 protected override String BuildCacheKey(String key) { 4 return String.Concat(_prefix, key); 5 } 6 7 protected override Boolean InnerTryGet(String key, out Object entry) { 8 Boolean exist = false; 9 entry = null;10 if (HttpContext.Current.Items.Contains(key)) {11 exist = true;12 entry = HttpContext.Current.Items[key];13 }14 return exist;15 }16 17 public override void Overwrite<T>(String key, T entry) {18 HttpContext.Current.Items[BuildCacheKey(key)] = entry;19 }20 21 public override void Expire(String key) {22 HttpContext.Current.Items.Remove(BuildCacheKey(key));23 }24 }這里不準(zhǔn)備為基于 HttpContext 的緩存提供太多特性,但基于 HttpRuntime.Cache 的緩存就需要像過(guò)期之類的功能,在實(shí)現(xiàn)之前先考慮問(wèn)題2。
首先,既然用戶沒(méi)有必要甚至不知道分區(qū)存在,我們直接實(shí)現(xiàn)支持分區(qū)特性的子類好了;然后,計(jì)數(shù)與過(guò)期功能 HttpRuntime.Cache 支持但 Memcached 不,所以這部分功能需要從 IHttpRuntimeCacheProvider 中拆分出來(lái),沒(méi)錯(cuò),擴(kuò)展方法!于是拆分如下:
1 public interface IRegion { 2 String Region { get; } 3 } 4 5 public interface IHttpRuntimeCacheProvider : ICacheProvider { 6 T GetOrCreate<T>(String key, Func<T> function, TimeSpan slidingExpiration); 7 T GetOrCreate<T>(String key, Func<T> function, DateTime absoluteExpiration); 8 void Overwrite<T>(String key, T value, TimeSpan slidingExpiration); 9 void Overwrite<T>(String key, T value, DateTime absoluteExpiration); 10 }其中IHttpRuntimeCacheProvider接口定義了帶有過(guò)期參數(shù)的緩存操作方法,我們需要實(shí)現(xiàn)抽象方法與額外接口如下:
1 public class HttpRuntimeCacheProvider : CacheProvider, IHttpRuntimeCacheProvider, IRegion { 2 private static readonly Object _nullEntry = new Object(); 3 private String _prefix = "HttpRuntimeCacheProvider_"; 4 5 public virtual String Region { get; private set; } 6 7 public HttpRuntimeCacheProvider() { 8 } 9 10 public HttpRuntimeCacheProvider(String region) {11 Region = region;12 }13 14 protected override bool InnerTryGet(String key, out object entry) {15 entry = HttpRuntime.Cache.Get(key);16 return entry != null;17 }18 19 protected override String BuildCacheKey(String key) {20 //Region 為空將被當(dāng)作 String.Empty 處理21 return Region == null22 ? String.Concat(_prefix, key)23 : String.Concat(_prefix, Region, key);24 }25 26 private Object BuildCacheEntry<T>(T value) {27 Object entry = value;28 if (value == null) {29 entry = _nullEntry;30 }31 return entry;32 }33 34 35 public T GetOrCreate<T>(String key, Func<T> function, TimeSpan slidingExpiration) {36 T value;37 if (TryGet<T>(key, out value)) {38 return value;39 }40 value = function();41 Overwrite(key, value, slidingExpiration);42 return value;43 }44 45 public T GetOrCreate<T>(String key, Func<T> function, DateTime absoluteExpiration) {46 T value;47 if (TryGet<T>(key, out value)) {48 return value;49 }50 value = function();51 Overwrite(key, value, absoluteExpiration);52 return value;53 }54 55 public override void Overwrite<T>(String key, T value) {56 HttpRuntime.Cache.Insert(BuildCacheKey(key), BuildCacheEntry<T>(value));57 }58 59 //slidingExpiration 時(shí)間內(nèi)無(wú)訪問(wèn)則過(guò)期60 public void Overwrite<T>(String key, T value, TimeSpan slidingExpiration) {61 HttpRuntime.Cache.Insert(BuildCacheKey(key), BuildCacheEntry<T>(value), null,62 Cache.NoAbsoluteExpiration, slidingExpiration);63 }64 65 //absoluteExpiration 時(shí)過(guò)期66 public void Overwrite<T>(String key, T value, DateTime absoluteExpiration) {67 HttpRuntime.Cache.Insert(BuildCacheKey(key), BuildCacheEntry<T>(value), null,68 absoluteExpiration, Cache.NoSlidingExpiration);69 }70 71 public override void Expire(String key) {72 HttpRuntime.Cache.Remove(BuildCacheKey(key));73 }74 75 internal Boolean Hit(DictionaryEntry entry) {76 return (entry.Key is String)77 && ((String)entry.Key).StartsWith(BuildCacheKey(String.Empty));78 }79 }HttpRuntimeCacheProvider 暴露了一個(gè) internal 修飾的方法,提供給擴(kuò)展方法調(diào)用:
1 public static
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注