ASP.NET 设计模式 读书摘记2
2013-02-24 15:43 Hejin.Wong 阅读(1280) 评论(0) 编辑 收藏 举报快速模式示例
代码:
public class Product { } 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(); } /// <summary> /// 从缓存中检索商品,若缓存为空,就从资源库中检索商品并将结果插入缓存中。 /// </summary> 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类中进行修改。
代码不可测试。两个类之间存在紧密的耦合。ProductRepository不连接真正的数据库,无法测试ProductService方法 该代码依赖于使用HTTP上下文来缓存商品。很难测试与HTTP上下文紧密耦合的代码。
被迫使用HTTP上下文来缓存。若使用Velocity或Memcached之类的缓存存储提供者,就需要修改代码。
重构
依赖倒置原则
依赖抽象而不要依赖具体。
ProductService依赖于ProductRepository类。如果ProductRepository类的API发生改变,就需要在ProductService类中进行修改。根据依赖倒置原则来解耦ProductService类和ProductRepository类,让他们都依赖于抽象——一个接口。
修改后代码(红色为变动 )
public class ProductRepository : IProductRepository { //…… } public class ProductService { private IProductRepository _productRepository; //…… }
这样ProductService 类只依赖于抽象而不是具体的实现。
ProductService类负责创建具体的实现,而目前没有有效的ProductRepository类的情况下不可能测试代码。
依赖注入原则(DI)
通过构造器、方法或属性来提供底层类或从属类。
配合使用DI原则,这些从属类可以被反转为接口或抽象类,这样就可以形成一个具有较高的可测试性和易于修改的松散耦合系统。
将创建ProductRepository实现的责任移到ProductService类以外,并通过该类的构造函数器注入。
修改ProductService类的构造方法:
public class ProductService { //…… public ProductService(IProductRepository productRepository) { _productRepository = productRepository; } //…… }
这样确保ProductService类遵循单一责任原则:只关心如何协调从缓存检索数据,而不是具体的IProductRepository的实现。
解决缓存需求对HTTPContext的依赖
没有HTTPContext类的源码,就不能想上面那样,简单的创建一个接口并让它实现该接口。
Adapter 适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本接口不匹配而无法在一起工作的两个类能够在一起工作。
//创建ICacheStorage的新接口 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 productRepository, ICacheStorage cacheStorage) { _productRepository = productRepository; _cacheStorage = cacheStorage; } public IList<Product> GetAllProductsIn(int categoryId) { IList<Product> products; string storageKey = string.Format("products_in_category_id_{0}", categoryId); products = _cacheStorage.Retrieve<List<Product>>(storageKey); if (products == null) { products = _productRepository.GetAllProductsIn(categoryId); _cacheStorage.Store(storageKey, products); } return products; } }
将HTTPContext缓存API修改成想要的兼容API,使用依赖注入原则,通过一个接口将缓存的API注入到ProductService类中。但是HTTPContext Cache API不能隐式的实现ICacheStorage接口。
Adapter模式中客户有一个对抽象(Target)的引用。Adapter是Target接口的一个实现。他将Operation方法委托给Adaptee类,该类运行自己的SpecificOperation方法。
我们新建一个HttpContextCacheAdapter类,是HttpContext缓存的包装器,并将工作委托给它的方法。
要实现Adapter我们至少HttpContextCacheAdapter类。
public class HttpContextCacheAdapter : ICacheStorage { public void Remove(string key) { HttpContext.Current.Cache.Remove(key); } public void Store(string key, object data) { HttpContext.Current.Cache.Insert(key, data); } public T Retrieve<T>(string key) { T itemStored = (T)HttpContext.Current.Cache.Get(key); if (itemStored == null) itemStored = default(T); return itemStored; } }
若使用Velocity或Memcached之类的缓存存储提供者,只需创建一个新的Adapter类即可。
Null Object(空对象)模式
当不希望指定或不能指定某个类的有效实例而且不希望到处传递null引用。null对象的作用是代替null引用并实现相同的接口但没有行为。
如不希望在ProductService类中缓存数据。
public class NullObjectCachingAdapter : ICacheStorage { public void Remove(string key) { // Do nothing } public void Store(string key, object data) { // Do nothing } public T Retrieve<T>(string storageKey) { return default(T); } }
将NullObjectCachingAdapter 传递给ProductService。当请求缓存数据时,他什么都不做而且总是想ProductService返回NULL,确保不会缓存任何数据。