業務邏輯層緩存應該設計
在業務制定的時候很少會切入緩存設計這一環節,畢竟在指標不明確的情況這屬于一種過渡設計.畢竟緩存切入有很多手段,在很多時候直接在WEB進行一個頁面緩存就有著非常高收益的效果.緩存是一種橫向的數據處理應用,一般在設計中引入AOP,ICO的應用組件都可以在后期切入添加.但AOP,ICO在沒有比較豐富的經驗情況引入會直接增加應用的復雜度和風險.在設計主要介紹一種簡單的設計方式在設計階段引用緩存但又不帶來復雜的工作成本.
一個簡單示例
public class BlogService:Interfaces.IBlogService { public IList<Blog> List(string category, int size, int index, out int pages) { Expression exp = new Expression(); if (!string.IsNullOrEmpty(category)) exp &= Blog.iD == BlogLinkCategory.blog[BlogLinkCategory.category == category]; int count = exp.Count<Blog>(); pages = count / size; if (count % size > 0) pages++; return exp.List<Blog>(new Region(index, size), Blog.createTime.Desc); } public IList<BlogCategory> ListCategories() { return new Expression().List<BlogCategory>(); } public Blog Get(string id) { return (Blog.iD == id).ListFirst<Blog>(); } }
以上是一個完全不考慮緩存應用的情況,一般在前期都這樣做,畢竟完成功能是首要面對的問題.
簡單加入緩存
以List方法為例加入緩存處理方式.
public IList<Blog> List(string category, int size, int index, out int pages) { IList<Blog> result = redis.Get<IList<Blog>>("key"); if (result == null) { Expression exp = new Expression(); if (!string.IsNullOrEmpty(category)) exp &= Blog.iD == BlogLinkCategory.blog[BlogLinkCategory.category == category]; int count = exp.Count<Blog>(); pages = count / size; if (count % size > 0) pages++; result = exp.List<Blog>(new Region(index, size), Blog.createTime.Desc); redis.Set("key", result); } return result; }
這一平時在開發中看到比較的方式,說實話一開始考慮這樣做的確是增加比較大的工作量,特別在前期階段沒有性能要求的情況,這只會增長工作和延時進度.還有就是緩存產品的偶合性也強達不到比較好的秀明度;畢竟開發人員還要學習具體緩存產品的API.
緩存實現的解偶
可以在設計的時候制定一個簡單的緩存儲訪問接口.
public interface ICached { T Get<T>(string key); void Set(string key, object data); }在設計的時候引入到接口定義中
public interface IBlogService { IList<Blog> List(string category, int size, int index, out int pages); Blog Get(string id); Blog Save(string id, string title, string keywords, string data, params string[] categories); IList<BlogCategory> ListCategories(); ICached Cached { get; set; } }這樣一個簡單針對邏輯層的Cached應用規范就出來了.這樣可以大大降低了開發人員對緩存產品依賴;當然這個接口設計的簡陋,在設計時有可能需要考慮超時設置等等.雖然解偶的目的達到了,但使用方便性上還是比較麻煩,工作并沒有多大的減少.
緩存規劃應用簡化
其實在接口針對Get定義一些簡單的委托參數可以簡單Cache在應用時的復發度.
public interface ICached { T Get<T>(Func<T> handler, string key); void Set(string key, object data); }那在編寫邏輯的時候就比較簡單一些
{ public IList<Blog> List(string category, int size, int index, out int pages) { dynamic eo = Cached.Get<dynamic>(() => { dynamic result = new ExpandoObject(); Expression exp = new Expression(); if (!string.IsNullOrEmpty(category)) exp &= Blog.iD == BlogLinkCategory.blog[BlogLinkCategory.category == category]; int count = exp.Count<Blog>(); result.Pages = count / size; if (count % size > 0) result.Pages++; result.Blogs = exp.List<Blog>(new Region(index, size), Blog.createTime.Desc); return result; }, key); pages = eo.Pages; return eo.Blogs; }
其實原理比較簡單,就是Get內部實現如果相關key不存在的情況直接執行Func<T>的方法得到數據,并設置到緩存中.以上需要處理一些條件和分頁返回所以感覺會復雜一些.一般代碼下都簡單.
public TextBlock GetByTitle(string title) { return Cached.Get<TextBlock>(() => (TextBlock.iD == title).ListFirst<TextBlock>(),title); }
總結
通過這樣的設計在邏輯層或其他層面引用緩存設計并不會對整個代碼帶來多大的復雜度變化,剛開始完全可以引用一個完全都不做的ICached實現.在后期有需要的話直接更換代碼.文章里的ICached的設計比較簡陋畢竟只是用于體現這種設計模式而已.如果業務場復雜的情況這個ICached所反映的行為參數可能會復雜一些.不過開發人員總會有辦法把復雜的調用變成簡單的.
個人站:www.ikende.com
個人開源項目github.com/IKende