在学习设计模式中,你是否也曾经拿着一本介绍23种设计模式,啃概念、uml、实现方式,但之后感觉是看与没看没什么区别,这里有个例子,足够简单地让人感觉到设计的好处;
例子实现的功能:根据一个分类返回所有的商品,并缓存
例如 京东,根据笔记本分类id http://list.jd.com/list.html?cat=670,671,672
几个类图关系如下:
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 查询所有商品的功能,这里有几个问题:
用设计模式与面向对象设计原则解决以上问题
重构后:
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, 点击前往