鸟随凤鸾,人伴贤良,得以共之,我之幸也。说的是鸟随着鸾凤可以飞的更高远,人和比自己境界高的相处,自己也会得到熏染进步。
一、概述
分享出来简单的心得,望探讨
依赖倒置
依赖注入
Adapter模式
Null模式
二、快速示例
从一个简单的示例开始,业务流程Client通过Service查询产品,基础大概框架如下
关键代码:
1 class ProductService 2 { 3 4 private ProductRepository _productRepository; 5 6 public ProductService() 7 { 8 _productRepository=new ProductRepository(); 9 } 10 11 public IList<Product> GerAllProductsIn(int category) 12 { 13 IList<Product> products; 14 string storagekey = string.Format("product_category_id_{0}",category); 15 products =(List<Product>) HttpContext.Current.Cache.Get(storagekey); 16 17 if (products==null) 18 { 19 products=_productRepository.GetAllProductsIn(category); 20 HttpContext.Current.Cache.Insert(storagekey,products); 21 } 22 return products; 23 } 24 }
ProductService直接依赖于ProductRepostory属于强耦合。如何理解这种强耦合呢?现在假设客户新产品上架,需要将ProductRepository替换为NewProductRepository,那么就势必要修改ProductService以及所有引用类ProductRepository,也就是高层模块依赖于低层模块。
三、重构
1、依赖倒置
那么究竟何为依赖倒置,它又如何解耦呢。
依赖倒置实际就是就是要依赖于抽象,不要依赖于具体。意思就是面对抽象进行,而不是面对实现进行编程,通过抽象解决客户与实现模块间的耦合关系。依赖倒置两大重要的原则:一是高层模块不应依赖于低层模块,都应依赖于抽象,二是具体实现也应依赖于抽象。
来看怎么做,定义IProductRepostory接口,使ProductRepostory依赖于接口 ,对ProductService重构也依赖于接口
1 class ProductService 2 { 3 4 private IProductRepository _productRepository; 5 6 public ProductService() 7 { 8 _productRepository=new ProductRepository(); 9 } 10 11 public IList<Product> GerAllProductsIn(int category) 12 { 13 IList<Product> products; 14 string storagekey = string.Format("product_category_id_{0}",category); 15 products =(List<Product>) HttpContext.Current.Cache.Get(storagekey); 16 17 if (products==null) 18 { 19 products=_productRepository.GetAllProductsIn(category); 20 HttpContext.Current.Cache.Insert(storagekey,products); 21 } 22 return products; 23 } 24 }
依赖倒置通过面对抽象从数据类型降低了耦合度,依赖倒置使高层模块依赖于抽象,即只关心new出来的是否是IProductRepository接口的对象而不关心new出来是谁。这样现在需要解决的就是具体实现的创建问题。
2、依赖注入
依赖注入从其初衷的角度解释就是:当类A完成某个工作需要创建B类的实例来协助的时候,把创建协助类B的工作提取出来从外部完成。
依赖注入三种形式:构造器,方法,属性。本文用构造器注入的方式介绍依赖注入
ProductService重构:
1 public class ProductService 2 { 3 private IProductRepository _productRepository; 4 5 6 public ProductService(IProductRepository productRepository) 7 { 8 _productRepository = productRepository; 9 10 } 11 12 .................. 13 14 15 }
测试代码(即外部):
1 namespace Patterns.QuickDemp.Tests 2 { 3 [TestFixture] 4 public class ProductServiceTests 5 { 6 [Test] 7 public void Test_Client() 8 { 9 //外部创建对象,任何继承抽象的对象皆可注入 10 AnyProductRepository anyproductrepository = new AnyProductRepository(); 11 //注入 12 ProductService productService = new ProductService(anyproductrepository); 13 ........... 14 .......... 15 16 } 17 18 19 } 20 }
可以大胆的作如下设想:客户,商家,第三方支付的关系拿来类比,客户直接与商家交易是一种模式。现在依赖反转,银行卡,支付宝,现金券等支付方式全部继承某个接口,支付动作抽象出来,客户选择支付方式然后外部注入,第三方根据注入指定方式完成支付动作。
3、Adapter模式
adapter模式意图描述:将一个类的接口转换成客户希望的接口
问题描述:用户自定义接口ICacheStorage.面向抽象过程中要让HttpContext成为一个实现用户自定义接口的实现。HttpContext是非用户编写的类,所以用到适配器模式
1 public interface ICacheStorage 2 { 3 void Remove(string key); 4 void Store(string key, object data); 5 T Retrieve<T>(string storageKey); 6 }
1 public class HttpContextCacheAdapter : ICacheStorage 2 { 3 public void Remove(string key) 4 { 5 HttpContext.Current.Cache.Remove(key); 6 } 7 8 public void Store(string key, object data) 9 { 10 HttpContext.Current.Cache.Insert(key, data); 11 } 12 13 public T Retrieve<T>(string key) 14 { 15 T itemStored = (T)HttpContext.Current.Cache.Get(key); 16 if (itemStored == null) 17 itemStored = default(T); 18 19 return itemStored; 20 } 21 }
仍然依赖倒置依赖注入对Service重构如下:
1 public class ProductService 2 { 3 private IProductRepository _productRepository; 4 private ICacheStorage _cacheStorage; 5 6 public ProductService(IProductRepository productRepository, ICacheStorage cacheStorage) 7 { 8 _productRepository = productRepository; 9 _cacheStorage = cacheStorage; 10 } 11 12 public IList<Product> GetAllProductsIn(int categoryId) 13 { 14 IList<Product> products; 15 string storageKey = string.Format("products_in_category_id_{0}", categoryId); 16 17 products = _cacheStorage.Retrieve<List<Product>>(storageKey); 18 19 if (products == null) 20 { 21 products = _productRepository.GetAllProductsIn(categoryId); 22 _cacheStorage.Store(storageKey, products); 23 } 24 25 return products; 26 } 27 }
4、Null Object 模式
空对象模式:在一些特殊的情况下用户不希望连带的执行某个动作,也不希望传递null在框架里做判断,就用到空对象模式。这个很容易理解,只是让附带动作变为do nothing
1 public class NullObjectCachingAdapter : ICacheStorage 2 { 3 public void Remove(string key) 4 { 5 // Do nothing 6 } 7 8 public void Store(string key, object data) 9 { 10 // Do nothing 11 } 12 13 public T Retrieve<T>(string storageKey) 14 { 15 return default(T); 16 } 17 }
四、小结
通过依赖注入和依赖倒置,把创建什么样对象的工作交给客户端(Client),降低Service、Repository、Cache之间的耦合度。假如有新的产品,只有实现了IRepositiry接口,在客户端创建对象,注入给service的构造函数就可以了。