“要针对接口编程,不要针对实现编程。”
陈述:
- 高层模块不应该依赖于低层模块。二者应该依赖于抽象。
- 抽象不应该依赖于细节。细节应该依赖于抽象。
- 所谓“倒置”是相对于传统的开发方法(例如结构化方法)中总是倾向于让高层模块依赖于低层模块而言的软件结构而言的。
- 高层包含应用程序的策略和业务模型,而低层包含更多的实现细节,平台相关细节等。高层依赖低层将导致:
- 难以复用。通常改变一个软硬件平台将导致一些具体的实现发生变化,如果高层依赖低层,这种变化将导致逐层的更改。
- 难以维护。低层通常是易变的。
- “……所有良构的OO体系结构都具有清晰的层次定义,每个层次通过一个定义良好的、受控的接口向外提供了一组内聚的服务。”
- 简单的理解
- 更好的理解
下层的实现,依赖于上层的接口
客户拥有接口,而服务者则从这些接口派生
当接受到Poll(轮询)消息时,判断其是否被“按下”。这个按下是抽象的(不关心通过什么样的机制去感知):
- 可能是GUI上的一个按钮被鼠标单击。
- 可能是一个真正的按钮被手指按下。
- 可能是一个防盗装置检测到了运动。
- ……
- 可能是计算机控制台的LED。
- 可能是停车场的日光灯。
- 可能是激光打印机中的激光。
- ……
- Lamp的任何变化都会影响到Button,导致其改写或者重新编译。
- 黑盒方式复用Button来控制一个Motor类变得不可能。
- classButton{
- Lamp*itsLamp;
- public:
- voidpoll(){
- if(/*somecondition*/)
- itsLamp->turnOn();
- ....
- }
- };
上面的设计违反了DIP。应用程序的高层策略没有和低层实现分离;抽象没有和具体细节分离。
改进思路
依赖于抽象
什么是高层策略?就是应用背后的抽象----背后的抽象是检测用户的开/关指令:
- 用什么机制检测用户的指令?无关紧要。
- 目标对象是什么?无关紧要。
- 他们不会影响到抽象的具体细节。
改进后的设计:
Button依赖于抽象的接口ButtonServer(向该接口发消息)。ButtonServer提供一些抽象的方法,Button类通过这些接口可以开启或关掉一些东西。
Lamp也依赖于ButtonServer接口(从此接口派生),提供具体的实现。
如下图所示:
部分代码:
- //Button.h
- #include"ButtonServer.h"
- classButton{
- ButtonServer*bs;
- public:
- voidpoll();
- };
- //Button.cpp
- voidButton::poll(){
- /*mechanismfordetectingturnOncommand*/)
- bs->turnOn();
- elseif((/*mechanismfordetectingturnOffcommand*/)
- bs->turnOff();
- }