前进中的蜗牛

番茄大叔

水滴穿石,非一日之功;没有量变,何来质变。

运用设计原则编写可测试性的代码

初入编程开发一两个项目后,渐渐发现自己编写的代码越往后越复杂,一个类承担太多的职责每次改动都需要先理清代码逻辑,还承担着很大风险。
接下来通过一个例子展示下这样编写可维护、可测试的代码。

需求

  • 根据Category查询Product集合
  • 对每次查询的结果加入缓存

不好的代码

	public class Product
	{
		public int Id { get; set; }
		public string Name { get; set; }
		public string Category { get; set; }
		public decimal Price { get; set; }
	}

    public class ProductRepository
	{
		public IList<Product> GetAllProductsIn(int categoryId)
		{
			IList<Product> products = new List<Product>();
			// Database operation to populate products …
			return products;
		}
	}

    public class ProductService
	{
		private ProductRepository _productRepository;
		public ProductService()
		{
			_productRepository = new ProductRepository();
		}
		public IList<Product> GetAllProductsIn(int categoryId)
		{
			IList<Product> products;
			string storageKey = string.Format(“products_in_category_id_{ 0}”, categoryId);
			products = (List<Product>)HttpContext.Current.Cache.Get(storageKey);
			if (products == null)
			{
				products = _productRepository.GetAllProductsIn(categoryId);
				HttpContext.Current.Cache.Insert(storageKey, products);
			}
			return products;
		}
	}

这种编程所带来的问题

  • ProductService依赖ProductRepository类,如果ProductRepository中API修改ProductService也必须修改
  • 代码不单元测试,因ProductService依赖ProductRepository和存储HttpContext.Current.Cache
  • 如果需要利用另外的方式缓存,需要修改ProductService中方法

健壮的代码

  • 依赖倒置原则,高层应依赖于抽象,不应依赖具体的底层。
  • 依赖注入,依赖的对象在使用时注入对象,这样可以Mock类单元测试。
    public class Product
	{
		public int Id { get; set; }
		public string Name { get; set; }
		public string Category { get; set; }
		public decimal Price { get; set; }
	}

    public interface IProductRepository
	{
		IList<Product> GetAllProductsIn(string categoryName);
	}

    public interface ICacheStorage
	{
		void Remove(string key);
		void Store(string key, object data);
		T Retrieve<T>(string storageKey);
	}

    public class ProductService
	{
		private IProductRepository _productRepository;
		private ICacheStorage _cacheStorage;

		public ProductService(IProductRepository productResitory, ICacheStorage cacheStorage)
		{
			_productRepository = productResitory;
			_cacheStorage = cacheStorage;
		}

		public IList<Product> GetAllProductsIn(string categoryName)
		{
			IList<Product> products;

			string storageKey = string.Format("products_in_category_id_{0}", categoryName);

			products = _cacheStorage.Retrieve<IList<Product>>(storageKey);

			if (products == null)
			{
				products = _productRepository.GetAllProductsIn(categoryName);
				_cacheStorage.Store(storageKey, products);
			}

			return products;
		}
	}

具体的仓储项目

public class DataContext
	{
		private List<Product> _products;

		public DataContext()
		{
			_products = new List<Product>();
			_products.Add(new Product { Id = 1, Name = "BaseBall Cap", Price = 9.99m, Category = "Hats" });
			_products.Add(new Product { Id = 2, Name = "Flat Cap", Price = 5.99m, Category = "Gloves" });
			_products.Add(new Product { Id = 3, Name = "Top Hat", Price = 6.99m, Category = "Scarfs" });

			_products.Add(new Product { Id = 4, Name = "Mitten", Price = 10.99m, Category = "Hats" });
			_products.Add(new Product { Id = 5, Name = "Fingerless Glove", Price = 13.99m, Category = "Gloves" });
			_products.Add(new Product { Id = 6, Name = "Leather Glove", Price = 7.99m, Category = "Scarfs" });

			_products.Add(new Product { Id = 7, Name = "Silk Scarf", Price = 23.99m, Category = "Scarfs" });
			_products.Add(new Product { Id = 8, Name = "Woollen", Price = 14.99m, Category = "Hats" });
		}
		public List<Product> Products
		{
			get { return _products; }
		}
	}

    public class ProductRepository:IProductRepository
	{
		private DataContext _dataContext;

		public ProductRepository()
		{
			_dataContext = new DataContext();
		}

		public IList<Product> GetAllProductsIn(string categoryName)
		{
			var result = _dataContext.Products.FindAll(i=>i.Category == categoryName);
			return result;
		}
	}

单元测试

参考下篇博客 AutoFac依赖注入应用

下载

posted @ 2018-07-09 18:44  LoveTomato  阅读(374)  评论(0编辑  收藏  举报