【Java与模式】依赖倒转原则

实现“开-闭”原则的关键是抽象化,并且从抽象化到处具体化实现。如果说"开-闭"原则是面向对象设计的目标的话,依赖倒转原则就是这个面向对象设计的主要机制。
依赖倒转原则讲的是:要依赖于抽象,不要依赖于具体。

一、为何而“倒转”

简单来说,传统的过程性系统的设计办法倾向于使高层次的模块依赖于底层的模块;抽象层次依赖于具体层次。倒转原则是要把这个错误的依赖关系倒转过来,就是“依赖倒转原则”的来由。

抽象层次依赖于具体层次是指:抽象层次包含的是应用系统的商务逻辑和宏观的、对整个系统来说重要的战略性决定,是必然性的体现;而具体层次则含有一些次要的与实现有关的算法和逻辑,以及战术性的决定,带来相当大的偶然性选择。具体层次的代码是会经常变动的,不能避免出现错误,抽象层次依赖于具体层次,使许多具体层次的细节的算法变化立即影响到抽象层次的宏观的商务逻辑,导致微观决定宏观,战术决定战略,偶然决定必然。这难道不是很荒唐吗?

抽象层次依赖于具体层次显然是不对的。依赖倒转原则就是要把错误的依赖关系再倒转过来。

依赖倒转原则是COM、CORBA、JavaBean以及EJB等构建设计模型背后的基本原则。

二、复用与可维护性的“倒转”

从复用的意义上讲,既然抽象层次含有一个应用系统最重要的宏观商务逻辑,是做战略性判断和决定的地方,那么抽象层次就应当是较为稳定的,应当是复用的重点。由于现有的复用侧重于具体模块和细节的复用,因此,“倒转”一词则是指复用的重点放在抽象层次上。如果抽象层次的模块相对独立于具体层次的模块的话,那么抽象层次的模块的复用便是相对比较容易的了。

同样,最重要的宏观商务逻辑也应当是维护的重点,而不是相反。

因此遵守依赖倒转原则会带来复用和可维护性的“倒转”.

三、依赖倒转原则

三种耦合关系

在面向对象系统里,两个类之间可以发生三种不同的耦合关系

  • 零耦合:如果两个类没有耦合关系,就称为零耦合。
  • 具体耦合:具体性耦合发生在两个具体的(可实例化的)类之间,经由一个类对另一个具体类的直接引用造成。
  • 抽象耦合:抽象耦合发生在一个具体类和一个抽象类之间,使两个必须发生关系的类之间存有最大的灵活性。

什么是依赖倒转原则

依赖倒转原则的表述是:
抽象不应当依赖于细节:细节应当依赖于抽象。
另一种表述是:
针对接口编程,不要针对实现编程。

针对接口编程的意思是说,应当使用Java接口和抽象Java类进行变量的类型声明、参量的类型声明、方法的返还类型声明,以及数据类型的转换等。

不要针对实现编程的意思就是说,不应当使用具体Java类进行变量的类型声明、参量的类型声明、方法的返还类型声明,以及数据类型的转换等。

要保证做到这一点,一个具体Java类应当只实现Java接口和抽象Java类中声明过的方法,而不应当给出多余的方法。

倒转依赖关系强调一个系统内的实体之间关系的灵活性。基本上,如果设计师希望遵守“开-闭”原则,那么倒转依赖原则便是达到要求的途径。

变量的静态类型和真是类型

变量被声明时的类型叫做静态类型,有些作者把静态类型叫做明显类型,变量所引用的对象的真实类型叫做变量的实际类型。
例如:

List employee = new Vector();

显然,在上面的代码中,employeeb变量的静态类型时list,而它的实际类型是Vector。

四、怎么做到依赖倒转原则

以抽象方式耦合是依赖倒转原则的关键。由于一个抽象耦合关键总要涉及具体类从抽象类继承,并且需要保证再任何引用到基类的地方都可以改换成其子类,因此,里氏代换原则是依赖倒转原则的基础。

在抽象层次上的耦合虽然有灵活性,但也带来了额外的复杂性。在某些情况下,如果一个具体类发生变化的可能非常小,那么抽象耦合能发挥的好处便十分有限,这时使用具体耦合反而会更好。

依赖倒转原则是OO设计的核心原则,设计模式研究和应用是以依赖倒转原则为指导原则的。

五、Java对抽象类型的支持

在Java语言中,可以定义一种抽象类型,并且提供这一抽象类型的各种具体实现。实际上,Java语言提供了两种而不是一种机制做到这一点。它们就是Java接口和Java抽象类。
Java接口与Java抽象类的区别如下:

  1. 这两者最明显的区别,就在于Java抽象类可以提供某些方法的部分实现,而Java接口则不可以,这大概是Java抽象类唯一的优点。如果向一个抽象类加入一个新的具体方法,那么所有的子类型一下子就都得到了这个新的具体方法,而Java接口做不到这一点。如果向一个Java接口加入一个新的方法的话,所有实现这个接口的类就不能全部成功地通过编译了,因为它们都没有实现这个新声明的方法。这显然是Java接口的一个缺点。
  2. 一个抽象类的实现只能由这个抽象类的子类给出的,也就是说,这个实现处在抽象类所定义出的继承的等级结构中,而由于Java语言限制一个类只能从最多一个超类继承,因此将抽象类作为类型定义工具的效能大打折扣。反过来看Java接口的情况,就会发现任何一个Java接口所规定的方法的类都可以具有这个接口的类型,而一个类可以实现任意多个Java接口。这便是这两者之间最为重要的区别。此外,Java接口还具有其他的优越性。
  3. 从代码重构的角度上讲,将一个单独的Java接口,并将重要的方法添加到接口声明中,然后在具体类定义语句后面加上一个合适的implements子句就可以了。而为一个已有的具体类型添加一个Java抽象类作为抽象类型却不那么容易,因为这个具体类有可能已经有一个超类。这样一来,这个新定义的抽象类只好继续向上移动,变成这个超类的超类,如此循环,最后这个新定义的抽象类必定处于整个类型等级结构的最上端,从而使等级结构中的所有成员都会受到影响。
  4. Java接口使定义混合类型的理想工具,所谓混合类型,就是在一个类的主类型之外的次要类型。一个混合类型表明一个类不仅仅具有某个主类型的行为,而且具有其他的次要行为。

联合使用Java接口和Java抽象类

由于Java抽象类具有提供缺省实现的优点,而Java接口具有其他的所有优点,所有联合使用两者就是一个很好的选择。

首先,声明类型的工作仍然是由Java接口承担的,但是同时给出的还有一个Java抽象类,为这个接口给出一个缺省实现。其他同属于这个抽象类型的具体类可以有选择的实现这个Java接口,也可以选择继承自这个抽象类。

如果一个具体类直接实现这个Java接口的话,它就必须自行实现所有的接口;相反,如果一个具体类继承自Java抽象类的话,它就可以省去一些不必要的方法,因为它可以从抽象类中自动得到这些方法的缺省实现。

如果需要向Java接口加入一个新的方法的话,那么只要同时向这个抽象类加入这个方法的一个具体实现就可以了,因为所有继承自这个抽象类的子类都会从这个抽象类得到这个具体方法。

这其实就是缺省适配模式。在Java语言API中也是用了这种缺省适配模式,而且全都遵循一定的命名规范:Abstract+接口名。如接口Collection,抽象类名字是AbstractCollection;Map和AbstractMap,List与AbstractList等。这种联合使用接口的抽象类的做法可以充分利用两者的优点,克服两者的缺点。

六、依赖倒转原则的优缺点

依赖倒转原则虽然很强大,但却是最不容易实现的。因为依赖关系倒转的缘故,对象的创建很可能要使用对象工厂,以避免对具体类的直接引用,此原则的使用还会导致大量的类。

此外,依赖倒转原则假定所有的具体类都是会变化的,这也不总是正确的。有一些具体类可能是相当稳定,不会发生变化的,消费这个具体类实例的客户端完全可以依赖于这个具体类型,而不必为此发明一个抽象类型。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值