利用依赖倒置和依赖注入实现应用程序解耦

首先,我们虚拟一个系统环境(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 : ICacheStoragepublic 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;        }    }}

附上重构后的设计图:

时间: 2024-10-10 23:02:54

利用依赖倒置和依赖注入实现应用程序解耦的相关文章

IoC模式(依赖、依赖倒置、依赖注入、控制反转)

1.依赖 依赖就是有联系,有地方使用到它就是有依赖它,一个系统不可能完全避免依赖.如果你的一个类或者模块在项目中没有用到它,恭喜你,可以从项目中剔除它或者排除它了,因为没有一个地方会依赖它.下面看一个简单的示例: /// <summary> /// 用户播放媒体文件 /// </summary> public class OperationMain { public void PlayMedia() { MediaFile _mtype = new MediaFile(); Pla

架构设计之依赖倒置、控制反转与依赖注入

名词解释 依赖:一种模型元素之间的关系的描述.例如类A调用了类B,那么我们说类A依赖于类B. 耦合:一种模型元素之间的关系的描述.例如类A调用了类B或类B调用了类A,那么我们说类A与类B有耦合关系. 耦合度:模型元素之间的依赖程度的量化描述. 控制:一种模型元素之间的关系的描述.例如类A调用了类B,那么我们说类A控制类B. 绪论 架构设计的对象一般是类库.框架和应用程序.其工作任务除了类库.框架.应用程序各个模块(类)之间的关系设计之外,还包括类库.框架和应用程序三者之间关系的设计.而依赖倒置.

Java设计原则—依赖倒置原则(转)

依赖倒置原则(Dependence Inversion Principle,DIP)的原始定义: 高层模块不应该依赖底层模块,两者都应该依赖其抽象: 抽象不应该依赖细节: 细节应该依赖抽象. 依赖倒置原则在Java语言中的表现是: 模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或者抽象类产生的: 接口或抽象类不依赖于实现类: 实现类依赖接口或抽象类. 依赖倒置原则实际上就是要求"面向接口编程". 采用依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,降

依赖倒置原则详解--七大面向对象设计原则(3)

依赖倒置原则来源: 类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成.这种场景下,类A一般是高层模块,负责复杂的业务逻辑:类B和类C是低层模块,负责基本的原子操作:假如修改类A,会给程序带来不必要的风险. 依赖倒置原则(Dependence Inversion Principle)是程序要依赖于抽象接口,不要依赖于具体实现.简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合. A.高层模块不应该依赖低层模块,二者都应该依赖其抽象.抽象

深入理解JavaScript系列(22):S.O.L.I.D五大原则之依赖倒置原则DIP

前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第5篇,依赖倒置原则LSP(The Dependency Inversion Principle ). 英文原文:http://freshbrewedcode.com/derekgreer/2012/01/22/solid-javascript-the-dependency-inversion-principle/ 依赖倒置原则 依赖倒置原则的描述是: A. High-level modules should not

六大设计原则(三)DIP依赖倒置原则

原文:六大设计原则(三)DIP依赖倒置原则 依赖倒置原则DIP(Dependence Inversion Principle) 依赖倒置原则的含义 高层模块不能依赖低层模块,二者都应该依赖其抽象. 抽象不应该依赖于细节. 细节应该依赖抽象. 什么是高层模块?低层模块? 每一个原子逻辑就是低层模块,原子逻辑再组就是高层模块. 什么是抽象和细节? 抽象是抽象类,不可被实例化. 细节是实现类,比如实现的接口或继承抽象类的子类,可以被实例化. 表现在Java语言中就是面向接口编程 模块间的依赖是通过抽象

依赖倒置原则:避免写出架构糟糕的代码

什么是依赖倒置原则 依赖倒置原则的原始定义为包含三个方面: 高层模块不应该依赖底层模块,两者都应该依赖其抽象 抽象不应该依赖细节 细节应该依赖抽象 高层模块和底层模块可能好理解些,因为每一个逻辑的实现都是由原子逻辑组成的,不可分割的原子逻辑就是低层模块,原子逻辑的再组装就是高层模块.那什么是抽象,什么是细节呢?我们不妨回到 Java 语言本身去找答案吧:在 Java 中,抽象指接口或抽象类,两者均不能被实例化:细节就是实现类,实现类继承抽象类或实现接口,特点在于可被实例化,所以依赖倒置原则在 J

增删改查也有设计模式——依赖倒置原则另解

一个增删改查的例子解读面向接口编程和依赖倒置原则 依赖倒置原则介绍 依赖倒置原则包括两个部分 .高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象. 抽象不应该依赖于具体实现,具体实现应该依赖于抽象. 例子 现在有如下场景和需求:老板要求设计任务模块,包括发布任务和撤回任务等.假设这个需求只给了几个小时去做,那肯定是来不及设计了,写到哪算哪.定义撤回接口的控制层如下 @RequestMapping('cancel') @ResponseBody public String cancelT

面向对象原则之一 依赖倒置原则

原文:面向对象原则之一 依赖倒置原则 前言 面向对象有人分为五大原则,分别为单一职责原则.开放封闭原则.依赖倒置原则.接口隔离原则.里氏替换原则. 也有人分为六大原则,分别为单一职责原则.开放封闭原则.依赖倒置原则.接口隔离原则.里氏替换原则.迪米特法则. 现在我们来介绍依赖倒置原则 依赖倒置原则 1)概念 a.高层模块不应该依赖于底层模块,两者应该依赖于其抽象. b.抽象不应该依赖具体实现,具体实现应该依赖抽象. 上面2点是依赖倒置原则的概念,也是核心.主要是说模块之间不要依赖具体实现,依赖接