Asp.Net Design Pattern Studynotes -- Part1
let's start with an exampleto entry amazing OO world !
let's saynow we need to implement a small feature which need :
an entityclass: Product
businesslogic : List<Product>GetProductBy(Function<Product,bool> where);
Service : List<Product> GetProductBy(Function<Product,bool>where);
1st Version
Entity:
public classProduct{}
Logic:
public classProductRepositoryV1 { public List<Product>GetProductBy(Func<Product,bool> where ) { var products = newList<Product>(); returnproducts.Where(where).ToList(); } }
service:
classProductServiceV1 { private ProductRepositoryV1 _productRespository; private List<Product>_cacheProduct ; public ProductServiceV1() { _productRespository = newProductRepositoryV1(); //1.instant _cacheProduct = newList<Product>(); } public List<Product>GetProductListBy(Func<Product,bool> where) { var productInCache =_cacheProduct.Where(where);//3.in couple with BL if (!productInCache.Any()) { var products =_productRespository.GetProductBy(where); _cacheProduct.AddRange(products); //2.also care about how to cache return products; } return productInCache.ToList(); } }
But we cansee the deficiencies :
For 1stVersion (Original Version):
1.productServiceis in couple with ProductRepository .once productRespository changed method signature, service have to change code.
solution: depend onabstract but not on concrete implementation
2.code is untestable .which need data base ready , but if data base can not connect ,meaning can not be tested.
solution: decoupleservice from business class
3.do multiple things .
a. cache thedata ,b. provice service .c. instance the repository object .
solution : do only one thing .
ok, nowlet's fix it !
2nd version :
Entity: same with above .
business:
interface IProductRespository//added interface fordependency inversion { List<Product>GetProductBy(Func<Product, bool> where); } class ProductRespositoryV2:IProductRespository { public List<Product>GetProductBy(Func<Product, bool> where) { var products = newList<Product>(); returnproducts.Where(where).ToList(); } }
Service :
class ProductServiceV2 { private IProductRespository _productRespository; private List<Product>_cacheProduct ; public ProductServiceV2(IProductRespositorypr) { _productRespository = pr; _cacheProduct = newList<Product>(); } public List<Product> GetProductListBy(Func<Product,bool> where) { var productInCache =_cacheProduct.Where(where); if (!productInCache.Any()) { var products =_productRespository.GetProductBy(where); _cacheProduct.AddRange(products); return products; } return productInCache.ToList(); } }
For 2ndVersion (Applydependency inversion + Dependency injection):
.still do multiple things:
a.still need to care about how to store . b. provide service
solution :put the responsibility of cache storage into another class,let service only depends on interface (IStorage)
3rd Version(Adapter pattern + Dependency inversion)
Entity :same with above .
business:
interface IProductRespository { List<Product> GetProductBy(Func<Product, bool> where); } interface IStorage { void Add(IEnumerable<Product>products); IEnumerable<Product> Get(Func<Product, bool> where); } class MemoryStorage:IStorage // Take the responsibility ofCache { private List<Product>_cacheProduct; public MemoryStorage() { _cacheProduct = newList<Product>(); } public void Add(IEnumerable<Product> products) { _cacheProduct.AddRange(products); } public IEnumerable<Product> Get(Func<Product, bool> where) { return _cacheProduct.Where(where); } } class ProductRespositoryV3:IProductRespository { public List<Product> GetProductBy(Func<Product, bool> where) { var products = new List<Product>(); return products.Where(where).ToList(); } }
Service:
class ProductServiceV3 { // only dependson abstract private IProductRespository_productRespository; private IStorage_cache; public ProductServiceV3(IProductRespository pr, IStorage storage) { //new objalso do not care _productRespository = pr; _cache = storage; } public List<Product> GetProductListBy(Func<Product,bool> where) { var productInCache = _cache.Get(where); if (!productInCache.Any()) { var products = _productRespository.GetProductBy(where); _cache.Add(products); return products; } return productInCache.ToList(); } }
We Can see ,
1.Service only depends on Interface which is abstract(no more need to care about how to cache the data) ,
2.and for Service, storage and respository class only do one thing
3.for service ,respository ,storage all can be UT .
Whenever weare coding any class,always remember these 3 things :
1.only do one thing
2.depends only on abstract
3.always can be tested