版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、依 赖 和 耦 合 关 系1、依赖和耦合(Dependency and Coupling)(1)什么是依赖Rose的帮助文档上是这样定义“依赖”关系的:“依赖描述了两个模型元素之间的关系,如果被依赖的模型元素发生变化就会影响到另一个模型元素。典型的,在类图上,依赖关系表明客户类的操作会调用服务器类的操作。”(2)什么是耦合Martin Fowler在Reducing Coupling一文中这样描述耦合:“如果改变程序的一个模块要求另一个模块同时发生变化,就认为这两个模块发生了耦合。” Fowler 2001注意:从上面的定义可以看出:如果模块A调用模块B提供的方法,或访问模块B中的某些数据成员
2、(当然,在面向对象开发中一般不提倡这样做),我们就认为模块A依赖于模块B,模块A和模块B之间发生了耦合。耦合是依赖的同义词,被定义为“两个元素之间的一种关系,其中一个元素变化,导致另一个元素变化”。抽象耦合被定义为“若类A维护一个指向抽象类B的引用,则称类A抽象耦合于B”。2、依赖是不可避免的(1)“分而治之”的问题处理方法对于复杂的系统,我们常常采用它划分成多个模块,这样将能够有效地控制模块的复杂度,使每个模块都易于理解和维护。(2)“分而治之”的结果是产生依赖关系l 一旦我们采用“分而治之”的处理方法后,模块之间就必须以某种方式交换信息,也就是必然要发生某种耦合关系。l 如果某个模块和其它
3、模块没有任何关联(哪怕只是潜在的或隐含的依赖关系),我们就几乎可以断定,该模块不属于此软件系统,应该从系统中剔除。l 如果所有模块之间都没有任何耦合关系,其结果必然是:整个软件不过是多个互不相干的系统的简单堆积,对每个系统而言,所有功能还是要在一个模块中实现,这等于没有做任何模块的分解。(3)依赖是不可避免的因此,模块之间必定会有这样或那样的依赖关系,我们永远也不要幻想消除所有的依赖(耦合关系)。我们在类的设计时,应该首先考虑的是该类应该尽可能依赖不经常变化的“目标”-比如,系统的API库或者框架中的组件-试图令具体的、易变的模块依赖于抽象的、稳定的模块的基本原则。在Java中,表示字符串的是
4、具体内String。该类是稳定的,也就是说,它不太会改变。因此,直接依赖于它不会造成损害。既然变化不可避免,我们所能做的就是处理好依赖关系,将变化造成的影响的波及范围尽量减小。(4)我们所追求的是尽可能降低过强的耦合关系“依赖”是和“变化”紧密联系在一起的概念。由于依赖关系的存在,变化在某处发生时,影响会波及开去,造成很多修改工作,这就是依赖的危害。l 因为,过强的耦合关系(如一个模块的变化会造成一个或多个其他模块也同时发生变化的依赖关系)会对软件系统的质量造成很大的危害。l 特别是当需求发生变化时,代码的维护成本将非常高。所以,我们必须想尽办法来控制和消解不必要的耦合,特别是那种会导致其它模
5、块发生不可控变化的依赖关系。(5)如何达到-采用什么的策略或者方法依赖倒置、控制反转、依赖注入等原则。3、依赖倒置(DIP-Dependency Inversion Principle)(1)什么是依赖倒置-将依赖关系倒置为依赖接口依赖倒置原则就是建立在抽象接口的基础上的。Robert Martin这样描述依赖倒置原则Martin 1996:l 上层模块不应该依赖于下层模块,它们共同依赖于一个抽象。l 抽象不能依赖于具体,具体依赖于抽象-也就是“系统的核心逻辑”不依赖于“具体的实现细节”。依赖性倒置原则形式化了抽象耦合的概念,明确表述了应该“依赖于抽象类,不要依赖于具体类”。(2)如何达到依赖
6、倒置的效果为了消解两个模块间的依赖关系,应该在两个模块之间定义一个抽象接口,上层模块调用抽象接口定义的方法,下层模块实现该接口。针对接口编程遵守上述原则,从而在很大程度上阻止了变化波及范围的扩大,有效地隔离了变化,有助于增强系统的可重用性和可扩展性。(3)为什么要依赖接口l 因为抽象是相对稳定的或者是相对变化不频繁的,而具体是易变的因此,要使我们的设计具有很大的灵活性,就需要做到大家都依赖于抽象的东西,而利用抽象隔离具体类之间的关系,这样使得“具体”的任何改动都可以在局部范围内实现,而不影响其它的结构。比如,一个IO系统它必然就有READ/WRITE这两个抽象,至于具体是磁盘还是键盘,那是下面
7、的实现不同了,通过这种构架,能保持软件的弹性与可维护性。l 依赖抽象是我们实现代码扩展和运行期内绑定(多态)的基础因为一旦类的使用者依赖某个具体的类,那么对该依赖的扩展就无从谈起;而依赖某个抽象类,则只要实现了该抽象类的子类,都可以被类的使用者使用,从而实现了系统的扩展。(4)示例(摘录网上资料)这里举个比较好笑的例子:一个即将做领导的儿子问曾经做领导的父亲怎么才能平步青云,父亲说你不能说假话,因为老百姓会不答应,也不能说实话,因为领导会对你有意见,儿子思索良久问:那我该说什么? 父亲意味深长的说:空话笑话不但好笑,还能反映一定的道理,为啥空话这么有用,因为他是抽象的,而抽象不涉及到一些具体的
8、数据或者事务,因此他是稳定的,是不容易变化的,同时基本上也是正确的。4、依据“依赖倒置原则”进行编程开发(1)体现倒置原则的UML类图以前传统的过程设计中是从上到下的一条依赖线现在是平的一条到接口,然后是一条从下往上的的关联线。(2)其主要的优点l 由于在依赖倒置中,通过抽象接口达到隔离了“服务的提供者”和“服务的请求者”,使它们之间没有直接的耦合关系,两者可以独立地扩展或重用。l 依赖倒置原则是许多面向对象技术的优点的根本。合理地应用它对于建立可复用的框架是必要的。对于构建富有变化弹力的代码也是相当重要的。而且,由于抽象和具体相互完全分离,代码的维护就容易很多了。依赖倒置原则一般应用于类与类
9、之间的代码级的依赖关系。(3)应用依赖倒置原则的代码示例l 任何实例对象变量都不应该持有一个指向具体类的引用,而应该为接口类型的对象声明class SomeClass private SomeInterface obj=null;l 任何类都不应该从具体类派生,而应该实现某个接口-面向接口编程,而不要面向实现编程class SomeClass implements SomeInterface / extends SuperClass5、依赖倒置原则示例(1)持久层中的各个组件常规的设计和实现方案在很多Web应用系统中,都需要对后台的数据库系统进行访问以实现数据存储的目的。而一个常见的解决手段是
10、使用分层的设计思想和方法,将Web应用系统中的持久层中的各个组件分为数据访问操作组件(DAO,Data Access Object)和数据连接组件,下面的图中所示为这种常规的设计和实现方案的类图。(2)常规设计和实现方案中所反映出的问题从上面的图中的所示的数据访问操作组件和数据连接组件之间的关系来看,两者产生了紧密的藕合关系。一旦数据连接组件有了任何变化,数据访问操作组件都有可能会受其影响而需要被改动。当然,如果只是针对这两个类本身而言的话,这种依赖关系根本算不上什么问题。然而,在一个实际的应用系统中,数据访问操作组件和数据连接组件都会由不只一个类而构成的,并都有一定的技术实现上的复杂度,这时
11、它们两者之间的这种依赖关系就会增加系统的复杂性;而且随着应用系统中的各个程序模块越来越多、功能实现也越来越复杂时,这种由于两者之间的紧密藕合所带来的系统的复杂度会越来越高。因此,如果不限制它们之间的紧密藕合联系,那模块间的依赖、程序的复杂度和开发维护的难度都会成指数上升。(3)对设计进行优化在两者之间添加一个抽象的接口在两者之间添加一个数据连接组件的接口,并在其中包含数据访问操作组件中所需要的各种数据连接的服务方法的定义,而某个具体的数据连接组件则实现这个接口。此时数据访问操作组件则只需要调用数据连接组件接口中的服务来实现数据库的各种连接功能,两者都只依赖于数据连接组件的接口。请见下面的图中所
12、示的优化设计结果的类图。这样,数据访问操作组件到数据连接组件的依赖关系被数据连接组件的接口所隔离开。因为在接口中只包含了所需要的各种连接的服务功能的定义,而不包括任何连接的服务功能的具体实现,所以数据连接组件的接口的内容在具体设计时也会很简单。在数据连接组件接口不变的情况下,数据访问操作组件和某个具体的数据连接组件这两个模块都可以自由地进行修改而不影响到对方(比如添加另一个数据连接组件),依赖关系也变得简单和可管理。(4)进一步改进本设计和实现方案核心逻辑不依赖于具体的实现细节如果从系统架构的角度来看上面的图中所示的设计和实现方案还存在一定的问题,因为数据访问操作组件所提供的功能是整个应用系统
13、的数据访问的核心部分,而数据连接组件中的各个功能方法则可以看成是具体实现的的细节。因此,从这个角度来看图中优化设计后的结果时,该设计是“核心逻辑依赖于具体的实现细节”。当然,也就没有遵守依赖倒置原则中的“抽象不能依赖于具体,具体依赖于抽象”的要求。因为,当细节变化(数据连接中的数据源或者连接方式发生变化)时,数据访问操作组件中的核心逻辑(数据访问的实现逻辑)也会受到一定的影响。因为当应用系统中的数据存储从某一种形式的数据库系统改变另一种形式的数据库系统(比如从微软的MS SQLServer2000改变为Oracle10G数据库系统)时,此时也将必然会影响到数据访问操作组件中有关的的具体实现方法
14、(因为对这两种不同的数据库系统在具体进行数据访问操作实现时的SQL语句或者涉及对存储过程的调用时,是不一样的!)。为了能够达到当系统中的数据库类型发生变化时,不至于影响到对数据库访问组件的使用者(业务层组件)的代码,有必要对系统设计进一步完善!下面的图所示为这样的应用场景下的各个类的设计要求的图示。(5)利用数据库访问操作的抽象接口进行隔离因此,有必要对上面的图中所体现出的设计结果进一步地改进和完善。主要的思路是将由于数据库连接方式的不同而造成的数据库访问操作的不同隔离开,在数据库访问操作这一层次同样也设计出一个数据库访问操作的抽象接口,并为该数据库访问操作的抽象接口提供不同的具体数据库访问操
15、作的实现类。这样的设计能够避免改动对数据库访问操作的使用类(一般为上层的业务层组件类)的代码,下面的图为进一步完善后的设计结果的类图。控制反转(Ioc-Inversion of Control)1、消解框架和我们的应用系统类之间的依赖关系前面依赖倒置原则描述的是类与类之间的代码级的依赖关系。如果我们开发的系统是基于某种框架系统来开发的,此时我们的类对目标框架的依赖关系就会更强烈一点。那么,该如何消解框架和我们的应用系统类之间的依赖关系呢?利用控制反转。2、控制反转(1)什么是控制反转“好莱坞原则(不要调用我们,让我们调用你)”。(2)面向框架和面向系统类库开发的不同点l 框架和类库最重要的区别
16、是:框架是一个半成品的应用程序,而类库只包含一系列可被应用程序调用的类。l 类库给用户提供了一系列可复用的类,这些类的设计都符合面向对象原则和模式。用户使用时,可以创建这些类的实例,或从这些类中继承出新的派生类,然后调用类中相应的功能。在这一过程中,类库总是被动地响应用户的调用请求。l 框架则会为某一特定目的实现一个基本的、可执行的架构。框架中已经包含了应用程序从启动到运行的主要流程,流程中那些无法预先确定的步骤留给用户来实现。程序运行时,框架系统自动调用用户实现的功能组件。这时,框架系统的行为是主动的。(3)应用系统和框架系统之间的依赖关系有以下特点l 应用系统和框架系统之间实际上是双向调用
17、,双向依赖的关系。l 通过依赖倒置原则可以减弱应用系统到框架系统之间的依赖关系。l 而利用“控制反转”及具体的模板方法模式可以消解框架到应用系统之间的依赖关系,这也是所有框架系统的基础。l 框架系统通过“控制反转”,最后能够达到可以独立地被重用。(4)“控制反转”的体现传统的面向类库的做法,我们在自己的程序中调用一个个类库去完成一个个功能,类库是我们的执行体,但是逻辑算法等是自己实现的,这就是自己的控制。但由于了框架后则不一样,逻辑算法都是它来实现,我们只要提供它几个需要的接口或者执行体就可。这就是反转,控制在框架这边了-由框架来对我们的代码进行调用。其目的主要是简化工作量,对于一些常见的业务
18、场景不需要自己去重复实现。3、应用框架是如何实现控制反转(1)利用模板方法模式为上层的应用系统提供“模板方法模式”,因为在面向对象领域,“回调函数”(一般是面向系统类库开发时采用的手段)的替代物就是“模板方法模式”( 一般是面向应用框架开发时采用的手段)。(2)什么是模板方法模式“模板方法模式”,也就是“好莱坞原则(不要调用我们,让我们调用你)”。模板方法模式是框架系统的基础,任何框架系统都离不开模板方法模式如Struts框架中的插件技术public final class UserDatabasePlugIn implements PlugInpublic void destroy() pu
19、blic void init(ActionServlet servlet, ModuleConfig config) throws ServletException(3)Spring 框架中的TransactionTemplate模板类的代码【例11-34】 TransactionTemplate模板类的部分代码示例package org.springframework.transaction.support;/ import语句,在此加以省略public class TransactionTemplate extends DefaultTransactionDefinition implem
20、ents InitializingBeanpublic Object execute(TransactionCallback action) throws TransactionException if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) return (CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);else Transactio
21、nStatus status =this.transactionManager.getTransaction(this);Object result = null;try /对用户的实现类中的doInTransaction方法进行回调 result = action.doInTransaction(status); catch (RuntimeException ex) /用户的回调方法执行失败时将自动地进行事务回滚rollbackOnException(status, ex);throw ex;catch (Error err) /用户的回调方法执行失败时将自动地进行事务回滚rollback
22、OnException(status, err);throw err; /用户的回调方法执行成功将自动地进行事务提交this.transactionMmit(status); return result;/ 其它的方法定义,在此加以省略在下面的【例11-35】中所示的是TransactionCallback接口的代码定义示例,该接口只有一个doInTransaction方法的声明。该方法也就是前面的TransactionTemplate模板类中的“回调”方法(或者是“钩子”方法)。【例11-35】TransactionCallback接口的定义的代码示例package org.springfr
23、amework.transaction.support;import org.springframework.transaction.TransactionStatus;public interface TransactionCallback Object doInTransaction(TransactionStatus status);4、“控制反转”和“依赖倒置”的不同点虽然“依赖倒置”和“控制反转”在设计层面上都是消解模块耦合的有效方法,也都是试图令具体的、易变的模块依赖于抽象的、稳定的模块的基本原则,但二者在使用语境和关注点上存在差异。(1)“倒置”的目标不同“依赖倒置”强调的是对于传统的、源于面向过程设计思想的层次概念的“倒置”,而“控制反转”强调的是对程序流程控制权的反转;(2)应用的范围不同“依赖倒置”的使用范围更为宽泛,既可用于对程序流程的描述(如流程的主从和层次关系),也可用于描述其他拥有概念层次的设计模型(如服务组件与客户组件、核心模块与外围应用等)。而“控制反转”则仅适用于描述流程控制权的场合(如算法流程或业务流程的控制权)。我们也可以
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024年国际酒店管理服务合作协议
- 二手住宅装修质量保修合同2024年版3篇
- 2024年多层合板标准购销协议版
- 2024全新鱼塘水库承包合同下载
- 江南大学《电磁场理论》2023-2024学年第一学期期末试卷
- 佳木斯大学《小学数学课程与教学》2021-2022学年第一学期期末试卷
- 佳木斯大学《即兴口语表达》2021-2022学年第一学期期末试卷
- 暨南大学《医学微生物》2021-2022学年第一学期期末试卷
- 济宁学院《新能源企业管理》2021-2022学年第一学期期末试卷
- 文旅新媒体运营 课件 第3章 文旅新媒体平台运营
- 《春江花月夜》课件24张
- 目标管理与执行计划课件
- 手诊,面诊课件
- 第九章-食品包装材料及容器的检测课件
- 物业服务交接方案
- 生产计划达成率管理办法
- 学生社会实践和社区服务活动记录表
- SURPAC软件地质建模操作步骤
- 上海8年级星级阅读训练Test1
- 悬吊治疗技术课件
- (完整版)工行收入证明(最新版)
评论
0/150
提交评论