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


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

  • 相关阅读:
    硬币游戏 Project Euler 232
    屏幕空间的近似全局光照明(Approximative Global Illumination in Screen Space)
    四维之美
    vertex texture fetching in HLSL, and heightfield normal calculation
    一个VS小插件(跳出括号)
    我的算法书籍收藏
    Algorithms.算法概论.习题答案
    UML用例图教程详解
    大连理工大学软件学院博客地址
    快递查询API,我推荐“爱快递”
  • 原文地址:https://www.cnblogs.com/xzwen/p/WhatPatterns_WhyItUsefully.html
Copyright © 2020-2023  润新知