敏捷软件开发 - 原则、模式与实践 —— 敏捷设计(五)依赖倒置原则

本文为敏捷软件开发 - 原则、模式与实践系列的一部分。

本文对应原书第11章。

依赖倒置原则(DIP - The Dependency-Inversion Principle)

a. 高层模块不应该依赖于低层模块。二者都应该依赖于抽象。
b. 抽象不应该依赖于细节。细节应该依赖于抽象。

请考虑一下当高层模块依赖于低层模块时以为着什么。高层模块包含了一个应用程序中的重要的策略选择和业务模型。正是这些高层模块才使得其所在的应用程序区别于其他。然而,如果这些高层模块依赖于底层模块,那么对于低层模块的改动就会直接影响到高层模块,从而迫使它们依次做出改动。这种情形是非常荒谬的!本应该是高层的策略设置模块去影响低层的细节实现模块的。包含高层业务规则的模块应该优先并独立于包含实现细节的模块。无论如何高层模块都不应该依赖于低层模块。

此外,我们更希望能够重用的是高层的策略设置模块。我们已经非常擅长于通过子程序库的形式来重用低层模块。如果高层模块依赖于低层模块,那么在不同的上下文中重用高层模块就会变得非常困难。然而,如果高层模块独立于低层模块,那么高层模块就可以非常容易地被重用。该原则是框架设计的核心原则。

层次化

每个较高层次都为它所需要的服务声明一个抽象接口,较低的层次实现了这些抽象接口,每个高层类都通过该抽象接口使用下一层,这样高层就不依赖于低层。低层反而依赖于在高层中声明的抽象服务接口。

请注意这里的倒置不仅仅是依赖关系的倒置,它也是接口所有权的倒置。我们通常会认为工具库应该拥有它们自己的接口。但是当应用了DIP时,我们发现往往是客户拥有抽象接口,而它们的服务者则从这些抽象接口派生。

依赖于抽象

一个稍微简单但仍然非常有效的对于DIP的解释,是这样一个简单的启发式规则:“依赖于抽象。”这是一个简单的陈述,该启发式规则建议不应该依赖于具体类 — 也就是说,程序中所有的依赖关系都应该终于于抽象类或接口。

当然,每个程序中都会有违反该启发规则的情况。有时必须要创建具体类的实例,而创建这些实例的模块将会依赖于它们。此外,该启发规则对于那些虽是具体但却稳定的类来说似乎不太合理。如果一个具体类不太会改变,并且也不会创建其他类似的派生类,那么依赖于它并不会造成损害。比如,字符串类String。

什么事高层策略呢?它是应用背后的抽象,是那些不随具体细节的改变而改变的真理。它是系统内部的系统 — 它是隐喻。

结论

使用传统的过程化程序设计所创建出来的依赖关系结构,策略是依赖于细节的。这是糟糕的,因为这样会使策略受到细节改变的影响。面向对象的程序设计倒置了依赖关系结构,使得细节和策略都依赖于抽象,并且常常是客户拥有服务接口。

事实上,这种依赖关系的倒置正是好的面向对象设计的标志所在。使用何种语言来编写程序是无关紧要的。如果程序的依赖关系是倒置的,它就是面向对象的设计。如果程序的依赖关系不是倒置的,它就是面向过程的设计。

依赖倒置原则是实现许多面向对象技术所宣称的好处的基本低层机制。它的正确应用对于创建可重用的框架来说是必须的。同时它对于构建在变化面前富有弹性的代码也是非常重要的。由于抽象和细节被彼此隔离,所以代码也非常容易维护。

完整内容请查看敏捷软件开发 - 原则、模式与实践系列

你可能感兴趣的:(软件设计,速读系列,敏捷开发)