使用设计模式,到底有什么好处?举例说明

在学习设计模式中,你是否也曾经拿着一本介绍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,点击前往

posted on 2015-03-22 00:06  向振文  阅读(3271)  评论(9编辑  收藏  举报