首先,来对比并分析一下结构化程序设计和面向对象程序设计的区别。
结构化程序设计总是倾向于创建一些高层模块依赖于低层模块、策略依赖于细节的软件结构,该层次结构描述了高层模块怎样调用低层模块。现在,我们就分析一下这样的程序结构中存在什么样的问题。高层模块将所需服务全权委托给底层模块。如果底层模块出现了问题,那么高层模块就被“小人”“出卖”了。因为,底层模块的变动影响了高层模块,高层模块受到了牵连。
我们更希望的是重用高层次的策略设置模块。如果高层模块依赖于低层模块,那么在不同的上下文中重用高层模块就会变得非常困难。如果高层模块独立于低层模块,那么高层模块就可以非常容易地被重用。著名的Hollywood原则这样写到:“Don’t call us,we’ll call you”。低层模块实现了在高层模块中声明并被高层模块调用的接口。这就是所谓的接口所有权倒置。面向对象程序设计的基本思路就是导致接口的所有权,让高层模块定义接口。使低层模块依赖于高层模块。
在软件中,可以使用这样的一个合适的模型来处理问题。每个较高层次都为他所需要的服务声明一个抽象接口,较低的层次实现了这个抽象的接口,每个高层类都通过该抽象接口使用下一层,这样高层就不依赖于低层。这样,不仅仅倒置了依赖关系,而且倒置了接口的所有权。
面向对象的程序设计倒置了依赖关系结构,使得细节和策略都依赖于抽象,并且常常是客户拥有服务接口。依赖倒置原则是实现许多面向对象技术宣称的好处的基本低层机制。对于构建在变化面前富有弹性的代码页是非常重要的。由于抽象和细节被彼此隔离,所以代码页非常容易维护。
下面就给出什么是【依赖倒置原则】。依赖倒置原则:
a) 高层模块不应该依赖于低层模块。二者都应该依赖于抽象
b) 抽象不应该依赖于细节。细节应该依赖于抽象。
有这样一个简单的启发规则:
l 任何变量都不应该持有一个指向具体类的指针或者引用
l 任何类都不应该从具体类派生
但是,通常会存在一些合理的违反这些规则的情况。比如对于具体但却稳定的类来说,直接引用就非常合理了。通常情况下,客户类声明它们需要的服务接口,仅当客户类需要时才会对接口进行改变。也就是说,有客户类来决定接口的变更。
接下来介绍一下什么是高层策略:它是应用背后的抽象,是那些不随具体细节的改变而改变的真理。
高层模块包含了一个应用程序中的主要策略选择和业务模型。整个程序中,业务模型和策略选择是核心,所以高层模块是程序的本质和灵魂所在。高层模块不能受到底层实现的影响。正确的方式应该是高层模块去影响低层的细节实现模块。包含高级业务规则的模块应该优先并独立于包含实现细节的模块。
最后,还是给出一个简单的例子来说明一下本文主要的意图。
现实中有这样的情景:使用开关来打开或关闭电灯。在这里就定义两个类:Button和Light。在Light类中,有这样的两个方法:turnOn()和turnOff()方法——表示电灯的打开或关闭。Button对象可以接受到用户打开或关闭的动作,然后调用Light的turnOn()或turnOff()方法。代码如下:
这里的问题Button类直接依赖于Light类。这样当Light类发生改变时,Button类就会受到影响。同样需求变化时,如果需要使用这个Button类控制门的打开和关闭,那这个Button类的代码几乎是不可重用的。
现在就改变这个程序结构,使Button类不依赖于具体的light实现。想想一下,如果想重用Button类,那么Button类需要什么样的服务呢?Button需要控制一个灯的打开或关闭,或者说门的打开或关闭。以后可能还会去控制空调等。这里面的本质或者说是高层策略就是Button类去控制一些东西的开与关。现在,从中提取出一个抽象接口Switchable,它表示一个事物有开和关的能力。使用Button类控制这个抽象接口,如果需要被Button类操作的事物都可以实现这个接口。代码如下:
使用上面的结构,很大程度上提高了代码的灵活性和可复用性。对于新增的任何需要被Button类控制的类,只需要继承Switchable接口即可,而无须对Button类做出任何改动。
最后总结一下结构化与面向对象之间的区别:无论使用什么语言来编写程序,如果程序的依赖关系是倒置的,它就是面向对象的设计。如果程序的依赖关系不是倒置的,它就是过程化的设计。
原文链接:https://www.f2er.com/javaschema/287765.html