業務邏輯層快取應該設計

smark發表於2014-09-15

        在業務制定的時候很少會切入快取設計這一環節,畢竟在指標不明確的情況這屬於一種過渡設計.畢竟快取切入有很多手段,在很多時候直接在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);

        }

        如果再花點小心思,那可以這樣子

  public User Get(string name)
        {

                return (User.name == name).ListFirst<User>();
        }

  public User Get(string name)
        {
            return Cached[name].Get<User>(() =>
            {
                return (User.name == name).ListFirst<User>();
            });
        }

        這樣基於在外面套一層Cached應用程式碼,裡面的程式碼是不用修改.

總結 

        通過這樣的設計在邏輯層或其他層面引用快取設計並不會對整個程式碼帶來多大的複雜度變化,剛開始完全可以引用一個完全都不做的ICached實現.在後期有需要的話直接更換程式碼.文章裡的ICached的設計比較簡陋畢竟只是用於體現這種設計模式而已.如果業務場複雜的情況這個ICached所反映的行為引數可能會複雜一些.不過開發人員總會有辦法把複雜的呼叫變成簡單的.

        

相關文章