zoukankan      html  css  js  c++  java
  • 利用依赖倒置和依赖注入实现应用程序解耦

    首先,我们虚拟一个系统环境(e-Shop),即网上商店的应用程序,其中有一个应用: 获取指定分类下的所有产品信息。我们按照传统的思路来实现。图1展示了系统的设计图。

     新建一个类库工程EShop.Service. 然后添加相应的类到工程中。

    原始程序
    namespace EShop.Service
    {
    public class Product
    {
    }

    public class ProductRepository
    {
    public IList<Product> GetProductsByCategory(int categoryId)
    {
    IList<Product> products = new List<Product>();
    //进行数据库操作
    return products;
    }
    }

    public class ProductService
    {
    private ProductRepository productRepository;

    public ProductService()
    {
    productRepository = new ProductRepository();
    }

    public IList<Product> GetProductsByCategory(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.GetProductsByCategory(categoryId);
    HttpContext.Current.Cache.Insert(storageKey, products);
    }
    return products;
    }
    }
    }

     从以上的程序段是否能发现不合理之处呢?我归纳了一下,大致有以下几点:

    1. ProductService依赖于ProductRepository,一旦后者的API发生变化,则前者必然跟着发生变化。

    2. 难以测试ProductService的方法,因为ProductRepository并未真正连接到数据库,另外还依赖于HttpContext,两者之间紧耦合。

    3. 目前采用HttpContext来进行缓存,如果要更换缓存机制(如:Velocity或Memcached),ProductService将进行更改。

    基于以上几点不合理之处,我们将一一进行重构和优化。

    针对1:我们采用依赖倒置原则(Dependency Inversion Principle)——依赖于抽象而不是具体实现来解决。我们加入了接口IProductRepository。

    增加接口
    public interface IProductRepository
    {
    IList<Product> GetProductsByCategory(int categoryId);
    }
    public class ProductRepository : IProductRepository
    {
    //...
    }
    public class ProductService
    {
    private IProductRepository productRepository;

    public ProductService()
    {
    productRepository = new ProductRepository();
    }
    //...
    }

    针对2:我们采用依赖注入原则(Dependency Injection Principle)——通过将抽象注入到构造函数、方法或属性来解决。我们修改ProductService构造函数。

    注入构造函数

    public class ProductService
    {
    private IProductRepository productRepository;

    public ProductService(IProductRepository productRepository)
    {
    this.productRepository = productRepository;
    }
    //...
    }

    针对3:我们采用适配器模式(Adapter Pattern)——转换已有接口与客户期望的目标接口使之兼容来解决。我们添加了接口ICacheStorage和类HttpContextCacheAdapter。

    采用适配器
    public interface ICacheStorage
    {
    void Remove(string key);
    void Store(string key, object data);
    T Retrieve<T>(string key);
    }

    public class HttpContextCacheAdapter : ICacheStorage
    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 item = (T)HttpContext.Current.Cache.Get(key);
    if (item == null)
    {
    item = default(T);
    }
    return item;
    }
    }

    public class ProductService
    {
    private IProductRepository productRepository;
    private ICacheStorage cacheStorage;

    public ProductService(IProductRepository productRepository, ICacheStorage cacheStorage)
    {
    this.productRepository = productRepository;
    this.cacheStorage = cacheStorage;
    }

    public IList<Product> GetProductsByCategory(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.GetProductsByCategory(categoryId);
    cacheStorage.Store(storageKey, products);
    }
    return products;
    }
    }

    综合以上的解决方案,这里贴出重构后完整的代码:

    重构后的代码
    namespace EShop.Service
    {
    public class Product
    {
    }

    public interface IProductRepository
    {
    IList<Product> GetProductsByCategory(int categoryId);
    }

    public class ProductRepository : IProductRepository
    {
    public IList<Product> GetProductsByCategory(int categoryId)
    {
    IList<Product> products = new List<Product>();
    //进行数据库操作
    return products;
    }
    }

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

    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 item = (T)HttpContext.Current.Cache.Get(key);
    if (item == null)
    {
    item = default(T);
    }
    return item;
    }
    }

    //有时为方便起见,我们定义空对象(空缓存适配器)
    public class NullCacheAdapter : ICacheStorage
    {
    public void Remove(string key)
    {
    }

    public void Store(string key, object data)
    {
    }

    public T Retrieve<T>(string key)
    {
    return default(T);
    }
    }

    public class ProductService
    {
    private IProductRepository productRepository;
    private ICacheStorage cacheStorage;

    public ProductService(IProductRepository productRepository, ICacheStorage cacheStorage)
    {
    this.productRepository = productRepository;
    this.cacheStorage = cacheStorage;
    }

    public IList<Product> GetProductsByCategory(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.GetProductsByCategory(categoryId);
    cacheStorage.Store(storageKey, products);
    }
    return products;
    }
    }
    }

    附上重构后的设计图:

  • 相关阅读:
    JPA条件查询时间区间用LocalDateTime的问题
    Java常用的异常类型
    Android 通用流行框架
    html图标插件
    炫酷科技
    使用zxing生成二维码
    八款常见的Android游戏引擎
    opengl es中不同的绘制方式
    Xml序列化去掉命名空间,去掉申明
    win8 app GridView点击子项布局变更
  • 原文地址:https://www.cnblogs.com/hmiinyu/p/2249059.html
Copyright © 2011-2022 走看看