使用設計模式,到底有什么好處?舉例說明


在學習設計模式中,你是否也曾經拿着一本介紹23種設計模式,啃概念、uml、實現方式,但之后感覺是看與沒看沒什么區別,這里有個例子,足夠簡單地讓人感覺到設計的好處;

例子實現的功能:根據一個分類返回所有的商品,並緩存
例如 京東,根據筆記本分類id http://list.jd.com/list.html?cat=670,671,672

幾個類圖關系如下:
uml

ProductService class:

    public class ProductService
    {
        private ProductRepository _productRepository;
        public ProductService()
        {
            _productRepository = new ProductRepository();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="categoryId"></param>
        /// <returns></returns>
        public IList<Product> GetAllProByCategoryId(int categoryId)
        {
            IList<Product> products;
            string cacheKey = string.Format("products_in_category_id_{0}",categoryId);
            products = (IList<Product>)HttpContext.Current.Cache.Get(cacheKey);
            if (products == null)
            {
                products = _productRepository.GetAllProByCategoryId(categoryId);
                HttpContext.Current.Cache.Insert(cacheKey, products);
            }
            return products;
        }
    }  

ProductRepository class:

    public class ProductRepository
    {
        public IList<Product> GetAllProByCategoryId(int categoryId)
        {
            IList<Product> products = new List<Product>();
            //get data for database
            return products;
        }
    } 

Product class:

    public class Product
    {
        public int id { get; set; }
        public string name { get; set; }
    }  

以上就簡單實現了根據分類id 查詢所有商品的功能,這里有幾個問題:

  1. ProductService依賴於ProductRepository,ProductRepository的修改會影響它。
  2. ProductService不可測試,必須先實現ProductRepository里面操作數據庫的方法,才能進行,緊耦合。
  3. 指定HTTP上下做緩存,之后難拓展,例如:之后需要換為Memcached或Redis做緩存,就要修改所有用到HTTP緩存的地方

用設計模式與面向對象設計原則解決以上問題
重構后:
ProductService class

 public class ProductService
    {
        //解決問題1,重構ProductRepository令其基於接口,這里依賴於接口,不依賴於具體類:《依賴倒置原則》
        private IProductRepository _productRepository;
        //解決問題3,因為沒有HTTP緩存的源碼,不能按照基於接口的方式重構,可以用適配器(Adapter)模式轉化為統一接口;
        private ICacheStorage _cacheStorage;
        //解決問題2,不創建實例,依賴外面傳入:《依賴注入原則》
        public ProductService(IProductRepository productRepository, ICacheStorage cacheStorage)
        {
            _productRepository = productRepository;
            _cacheStorage = cacheStorage;
        }
        /// <summary>
        /// 獲取一個分類下的所有商品
        /// </summary>
        /// <param name="categoryId"></param>
        /// <returns></returns>
        public IList<Product> GetAllProByCategoryId(int categoryId)
        {
            IList<Product> products;
            string cacheKey = string.Format("products_in_category_id_{0}", categoryId);
            //products = (IList<Product>)HttpContext.Current.Cache.Get(cacheKey);
            products = _cacheStorage.Get<IList<Product>>(cacheKey);
            if (products == null)
            {
                products = _productRepository.GetAllProByCategoryId(categoryId);
                //HttpContext.Current.Cache.Insert(cacheKey, products);
                _cacheStorage.Add(cacheKey, products);
            }
            return products;
        }
    } 

IProductRepository:

    public interface IProductRepository
    {
        IList<Product> GetAllProByCategoryId(int categoryId);
    }

ProductRepository:

    public class ProductRepository : IProductRepository
    {
        public IList<Product> GetAllProByCategoryId(int categoryId)
        {
            IList<Product> products = new List<Product>();
            //get data for database
            return products;
        }
    }  

ICacheStorage :

    public interface ICacheStorage
    {
        void Delete(string key);
        void Add(string key, object data);
        T Get<T>(string key);
    }  

HttpCacheAdapter :

    public class HttpCacheAdapter :ICacheStorage
    {
        public void Delete(string key)
        {
            HttpContext.Current.Cache.Remove(key);
        }
        public void Add(string key, object data)
        {
            HttpContext.Current.Cache.Insert(key, data);
        }
        public T Get<T>(string key)
        {
            return (T)HttpContext.Current.Cache.Get(key);
        }
    }  

最后,添加一個ProductRepository模擬返回數據

     public class ProductRepository_ForTest : IProductRepository
    {
        /// <summary>
        /// 在數據庫操作未完成情況下,使用返回模擬數據,可以繼續測試Service層的邏輯;
        /// </summary>
        /// <param name="categoryId"></param>
        /// <returns></returns>
        public IList<Product> GetAllProByCategoryId(int categoryId)
        {
            IList<Product> products = new List<Product>();
            Product one = new Product();
            one.id = 1;
            one.name = "AA";
            products.Add(one);
            one = new Product();
            one.id = 2;
            one.name = "BB";
            products.Add(one);
            return products;
        }
    } 

兩個方式如何使用呢?
使用控制台調用例子:

    class Program
    {
        static void Main(string[] args)
        {
            //====== 沒重構前調用 =======
            int category = 1;
            NoPatterns.ProductService noPatternsService = new NoPatterns.ProductService();
            IList<NoPatterns.Product> products = noPatternsService.GetAllProByCategoryId(category);
            //======= 重構后的調用 ======
            //在數據庫操作未完成情況下,可使用返回模擬數據;
            YesPatterns.ProductRepository_ForTest productRepository = new ProductRepository_ForTest();
            //基於數據庫真實操作;  
            //YesPatterns.ProductRepository productRepository = new YesPatterns.ProductRepository();
            //這樣做的好處:數據庫未准備好,也可以完成並測試Service層的邏輯,不用依賴;

            //使用http上下緩存
            YesPatterns.HttpCacheAdapter cache = new HttpCacheAdapter();
            //使用Memcached緩存;
            //YesPatterns.MemCachedAdapter cache = new MemCachedAdapter();
            //這樣做的好處:方便拓展,靈活,例如網站訪問量大了,使用Http上下文緩存會力不從心,可以方便換為分布式的緩存,例如Memcached
            //再例如:可以兩種緩存方式一起使用。與用戶相關緩存,使用Http;全局通用的緩存用Memcached;

            YesPatterns.ProductService yesPatternsService = new YesPatterns.ProductService(productRepository, cache);
            yesPatternsService.GetAllProByCategoryId(category);
        }
    }  

完整例子代碼已經放到github,點擊前往


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM