高内聚,低耦合_第1页
高内聚,低耦合_第2页
高内聚,低耦合_第3页
高内聚,低耦合_第4页
高内聚,低耦合_第5页
已阅读5页,还剩9页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、对高内聚,低耦合的理解内聚:一个模块内各个元素彼此结合的紧密程度耦合:一个软件结构内不同模块之间互连程度的度量(耦合性也叫块间联系。指 软件系统结构中个模块间相互联系紧密程度的一种度量。模块之间联系越紧密, 其耦合性就越强,模块的独立性则越差,模块间耦合的高低取决于模块间接口的 复杂性,调用的方式以及传递的信息。)最近编码的时候,总是在犹豫是把某个方法封装在一个类里,还是单独的封装成 一个类。这让我突然想起内聚耦合这两个名词。我们一直追求着,高内聚,低耦合。对于低耦合,粗浅的理解是:一个完整的系统,模块与模块之间,尽可能的使其独立存在。也就是说,让每个模块,尽可能的独立完成某个特定的子功能。模

2、块与模块之间的接口,尽量的少而简单。如果某两个模块间的关系比较复杂的话,最好首先考虑进一步的模块划分。这样有利于修改和组合。对于低耦合,我粗浅的理解是:在一个模块内,让每个元素之间都尽可能的紧密相连。也就是充分利用每一个元素的功能,各施所能,以最终实现某个功能。如果某个元素与该模块的关系比较疏松的话,可能该模块的结构还不够完善,或 者是该元素是多余的。内聚和耦合,包含了横向和纵向的关系。功能内聚和数据耦合,是我们需要达成 的目标。横向的内聚和耦合,通常体现在系统的各个模块、类之间的关系,而纵 向的耦合,体现在系统的各个层次之间的关系。对于我在编码中的困惑,我是这样想的,用面向对象的思想去考虑一

3、个类的封装。 一个方法,如何封装,拿到现实生活中来看,看这种能力(方法)是否是属于这 类事物(类)的本能。如果是,就封装在这个类里。如果不是,则考虑封装在其它类里。如果这种能力,很多事物都具有,则一定要封装在这类事物的总类里。如果这种能力,很多事物都会经常用到,则可以封装成一个总类的静态方法。关于耦合内聚的概念这些是软件工程中的知识,我上网查过,总结着几位大虾的评论,关于耦合的概念 应该是这样的:对象之间的耦合度就是对象之间的依赖性.指导使用和维护对象的主要问题是 对象之间的多重依赖性.对象之间的耦合性越高.维护成本越高.因此对象的设计 应使类和构件之间的耦合最小.耦合性是程序结构中各个模块之

4、间相互关联的度量.它取决于各个模块之间的 接口的复杂程度,调用模块的方式一级哪些信息通过接口,一般模块之间可能的 连接方式有七种,耦合性由低到高分别是:非直接耦合,数据耦合,标记耦合,控制 耦合,外部耦合,公共耦合,内容耦合.一个软件是由多个子程序组装而成,而一个程序由多个模块(方法)构成.耦合是指各个外部程序(子程序)之间的关系紧密度而内聚就是指程序内的各个模块之间的关系紧密度所以说,为什么要高内聚,模块之间的关系越紧密,出错就越少!低耦合就是说,子 程序之间的关系越复杂,就会产生出更多的意想不到的错误!会给以后的维护工 作带来很多麻烦一个优秀软件开发人员的必修课:GRASP你是一个优秀软件

5、开发人员吗?你知道GRASP吗? GRASP软件开发模式,全 称通用职责分配软件模式(General Responsibility Assignment Software Patterns),是与著名的软件模式GoF(Gang of Four,即我们常说的那23种 软件开发模式)齐名的另一种软件开发模式。但是与GoF不同的是,它并不是 提出一些具体的软件组织结构,而是提出,在将现实世界的业务功能抽象成软件 开发中具体对象的过程中,我们应当遵循的一些基本原则。遵循这些基本原则, 我们才可以开发出高质量的软件出来。对于我们要开发的软件项目,我们可以不 使用工厂模式、可以不使用单例模式、我们也可以不

6、使用观察者模式,但是我 们不可能不将现实世界的业务功能抽象成软件开发中具体对象。从这个角度说, 我们要提高自己的软件开发质量,深入理解GRASP比深入理解GoF更重要。但是 我看到现在介绍GoF的文章多,介绍GRASP文章少。正因为如此,我现在把GRASP 介绍给大家。GRASP包含了 9个模式,也就是9个基本原则。这在软件设计大师Craig Larman的经典著作UML和模式应用中进行了深入地讲解。GRASP叫通用职责分配软 件模式要理解GRASP,我们首先必须理解的一个问题是,我们在对象分析和设计 过程中为什么要进行职责分配。一.职责分配和职责驱动设计在一个软件项目开始的时候,我们通常需要

7、进行需求分析,了解客户需要 设计一个什么样的软件,这个软件中应当有什么功能。需求分析了解到的是现实 世界中客户需求的业务功能,每个业务功能往往是一个业务流程,即客户在日 常工作中不断在完成的业务流程。同时,在用户的问题世界中,必然有一些东西 或者说事物,它们之间存在着相互的关联。拿一个软件评审管理系统作为一个例子吧。评审管理系统的业务需求如下:评审组织者制订评审计划,提交领导审批,然后通过邮件通知评审者。评审者接到通知,分别对评审对象进行评审,填写评审表,并可以对评 审对象提出疑问。评审组织者汇总评审者的疑问,召开评审会议讨论这些疑问。在会上, 有些疑问变为问题,有些疑问不是问题,有些则依然不

8、能够确认。会后,评审组织者整理疑问,形成评审报告,然后由评审者分别表决该 评审是否通过。最后评审组织者汇总表决结果,形成评审结论。评审组织者跟踪问题的解决。通过以上需求的描述,我们不难发现整个问题世界中的相关事物:评审组 织者、评审计划、评审者、评审对象、评审表、疑问、评审报告、评审结论、问 题。我们也不难分析出这些事物相互关系,比如评审计划与评审者是一对多,而 评审报告与评审结论是一对一。在RUP中,业务需求将形成用例模型及其描述文档中的用例,事物及其关 系将形成领域模型中的对象,当然如何制作用例模型和领域模型超出了本文讨论 的范围,有兴趣的朋友可以看看相关文章。领域模型中的对象将成为软件开

9、发中形成具体对象的基础(软件开发中形 成什么对象是根据软件开发的具体需求而定的,并不一定要与领域模型的对象一 致)。用例模型中的用例,将通过赋予这些对象行为 而得以实现。现在的问题 就出来了,用例模型中的功能,或者说一系列行为,应当如何分配给这些对象呢。 也就是说,为了完成同一个任务,我可以将行为A交给对象X,也可以交给对象 Y。虽然交给对象X与交给对象Y,我对行为A的具体实现不一样,但是都可以 完成行为A的任务。那么,我到底应当交给对象X还是对象Y呢?有没有一个 基本原则呢?有,那就是按照职责分配任务。虽然从理论上说,我可以任意定义 对象,可以让对象没有任何意义,或者去完成任意的工作,但是通

10、常我们不会 这样去设计。通常我们会将对象与现实世界的对象联系起来,比如设计一个评审 计划对象、评审者对象。并且我们在设计对象的时候应当做到“低表示差异”。 低表示差异就是我们设计的对象应当与现实世界的对象尽量一致。比如说我们设 计一个对象叫“评审者”,是因为我们在现实世界中有评审者。同时,我们为评 审者对象赋予的行为也应当尽量与现实世界一致,比如增加评审者、修改评审 者、得到评审者信息。那么哪些是这个对象应当赋予的行为呢,这应当由职责来 决定。我们通过对现实世界的分析,或者说对于领域模型的分析,设计出了软件 系统中的对象,这时候我们应当为每一个对象分配职责。什么是对象的职责呢, 当然是通过对现

11、实世界的分析,定义的这个对象应当完成的任务。比如评审者 对象的职责是存取与评审者相关的数据。当然对象的职责不一定是一个,比如评 审计划包含了评审对象和评审者的子项,所以它在工作不繁忙的情况下可以代 理处理评审对象和评审者的信息存取。但是一个对象的职责不应当过多(也就2、 3个就行了)并且高度相关。比如评审表对象如果分配职责处理评审表的同时, 又去处理评审计划的数据,这就叫职责无关。职责分配现在已经被普遍认为是一个优秀的软件设计应当遵循的原则,它 有以下好处:1.低表示差异使软件结构清晰,易于理解,因为软件开发并不是一个人的 事情。在多人共同开发的软件项目中,一目了然的软件结构可以避免开发人员因

12、 误解而造成的不必要错误。2.易于维护和变更。假如评审计划出了问题或需要修改,我们就去找评审 计划,如果是评审者的问题我们就去找评审者,而绝对不会与其它对象有关。这种通过考虑对象、职责、协作的对象设计及构件方式,被称为“职责驱 动设计(RDD,Responsibility Drive Design)”。职责驱动设计是通过先设 计用例模型、用例模型描述、操作契约、系统顺序图、领域模型、词汇表,再一 步步制作分析模型、设计模型,写出每个功能的交互图、类图的很复杂的过程, 我在这里就不再详述了。但是请大家注意一个非常重要的细节,前面我们说,软 件系统中的对象是根据现实世界抽象得到,对象职责的分配是根

13、 据对象的定义, 分配一些不多并且高度相关的任务。然而我们即使遵照这些原则,也有相当大的 设计弹性空间,不同人根据自己的理解,对于同一个功能依然有各自不同的设 计。GRASP中文译为“通用职责分配软件模式”,就是对对象分析和设计中职责 分配问题提出数个基本原则。同时,这几个基本原则也应当掌握一个度,即并 不是所有情况下都适用,也不是一个绝对的指标。比如低耦合,并不是绝对的不 耦合,不耦合软件 就没法设计了;高内聚也不能无限度地高内聚,否则系统就 繁复异常了。一个优秀软件开发人员的必修课:GRASP (2)低耦合关键字:设计模式 我偶然在google或yahoo这样的搜索引擎搜索GRASP发现,

14、除了国外的网站,国内网站多 介绍和讨论GoF而很少介绍GRASP,即使这少量的文章也讲解非常粗略。个人认为作为优 秀的开发人员,理解GRASP比GoF更重要,故写此文章。前面我在(原创)一个优秀 软件开发人员的必修课:GRASP软件开发模式浅析中介绍了使用GRASP的目的,今天 允许我调换一下顺序,先从低耦合讲起,因为诸如创建者模式、信息专家模式的根本目的就 是降低耦合。低耦合(Low Coupling)“低耦合”这个词相信大家已经耳熟能详,我们在看spring的书籍、MVC的数据、设计模 式的书籍,无处不提到“低耦合、高内聚”,它已经成为软件设计质量的标准之一。那么什 么是低耦合?耦合就是对

15、某元素与其它元素之间的连接、感知和依赖的量度。这里所说的元 素,即可以是功能、对象(类),也可以指系统、子系统、模块。假如一个元素A去连接元 素B,或者通过自己的方法可以感知B,或者当B不存在的时候就不能正常工作,那么就说 元素A与元素B耦合。耦合带来的问题是,当元素B发生变更或不存在时,都将影响元素 A的正常工作,影响系统的可维护性和易变更性。同时元素A只能工作于元素B存在的环 境中,这也降低了元素A的可复用性。正因为耦合的种种弊端,我们在软件设计的时候努 力追求“低耦合”。低耦合就是要求在我们的软件系统中,某元素不要过度依赖于其它元素。 请注意这里的“过度”二字。系统中低耦合不能过度,比如

16、说我们设计一个类可以不与JDK 耦合,这可能吗?除非你不是设计的Java程序。再比如我设计了一个类,它不与我的系统 中的任何类发生耦合。如果有这样一个类,那么它必然是低内聚(关于内聚的问题我随后讨 论)。耦合与内聚常常是一个矛盾的两个方面。最佳的方案就是寻找一个合适的中间点。哪些是耦合呢?元素B是元素A的属性,或者元素A引用了元素B的实例(这包括元素A调用的某个 方法,其参数中包含元素B)。元素A调用了元素B的方法。元素A直接或间接成为元素B的子类。元素A是接口 B的实现。幸运的是,目前已经有大量的框架帮助我们降低我们系统的耦合度。比如,使用struts我们 可以应用MVC模型,使页面展现与业

17、务逻辑分离,做到了页面展现与业务逻辑的低耦合。 当我们的页面展现需要变更时,我们只需要修改我们的页面,而不影响我们的业务逻辑;同 样,我们的业务逻辑需要变更的时候,我们只需要修改我们的java程序,与我们的页面无 关。使用spring我们运用IoC (反向控制),降低了业务逻辑中各个类的相互依赖。假如类 A因为需要功能F而调用类B,在通常的情况下类A需要引用类B,因而类A就依赖于类 B 了,也就是说当类B不存在的时候类A就无法使用了。使用了 IoC,类A调用的仅仅是 实现了功能F的接口的某个类,这个类可能是类B,也可能是另一个类C,由spring的配置 文件来决定。这样,类A就不再依赖于类B

18、了,耦合度降低,重用性提高了。使用hibernate 则是使我们的业务逻辑与数据持久化分离,也就是与将数据存储到数据库的操作分离。我们 在业务逻辑中只需要将数据放到值对象中,然后交给hibernate,或者从hibernate那里得到 值对象。至于用Oracle、MySQL还是SQL Server,如何执行的操作,与我无关。但是,作为优秀的开发人员,仅仅依靠框架提供的降低软件耦合的方法是远远不够的。根据 我的经验,以下一些问题我们应当引起注意:1)根据可能的变化设计软件我们采用职责驱动设计,设计中尽力做到“低耦合、高内聚”的一个非常重要的前提是,我 们的软件是在不断变化的。如果没有变化我们当然

19、就不用这么费劲了;但是如果有变化,我 们希望通过以上的设计,使我们在适应或者更改这样的变化的时候,付出更小的代价。这里 提供了一个非常重要的信息是,我们努力降低耦合的是那些可能发生变更的地方,因为降 低耦合是有代价的,是以增加资源耗费和代码复杂度为代价的。如果系统中某些元素不太 可能变更,或者降低耦合所付出的代价太大,我们当然就应当选择耦合。有一次我试图将 我的表现层不依赖于struts,但发现这样的尝试代价太大而失去意义了。对于软件可能变更 的部分,我们应当努力去降低耦合,这就给我们提出一个要求是,在软件设计的时候可以预 判日后的变化。根据以往的经验我认为,一个软件的业务逻辑和采用的技术框架

20、往往是容易 变化的2个方面。客户需求变更是我们软件设计必须考虑的问题。在RUP的开发过程中, 为什么需要将分析设计的过程分为分析模型和设计模型,愚以为,从分析模型到设计模型的 过程实际上是系统从满足直接的客户需求到优化系统结构、适应可预见的客户需求变更的一 个过程。这种客户需求的变更不仅仅指对一个客户需求的变更,更是指我们的软件从适应一 个客户需求到适应更多客户需求的过程。另一个方面,现在技术变更之快,EJB、hibernate、 spring, ajax,一个一个的技术像走马灯一样从我们脑海中滑过,我们真不知道明天我在用 什么。在这样的情况下,适应变化就是我们最佳的选择。2)合理的职责划分合

21、理的职责划分,让系统中的对象各司其职,不仅是提高内聚的要求,同时也可以有效地降 低耦合。比如评审计划BUS、评审表BUS、评审报告BUS都需要通过评审计划DAO去查 询一些评审计划的数据,如果它们都去直接调用评审计划DAO (如图A),则评审计划BUS、 评审表BUS、评审报告BUS三个对象都与评审计划DAO耦合,评审计划DAO 一旦变更将 与这三个对象都有关。在这个实例中,实际上评审计划BUS是信息专家(关于信息专家模 式我将在后面讨论),评审表BUS和评审报告BUS如果需要获得评审计划的数据,应当向 评审计划BUS提出需求,由评审计划BUS提供数据(如图B)。经过这样的调整,系统的 耦合度

22、就降低了。3)使用接口而不是继承通过对耦合的分析,我们不难发现,继承就是一种耦合。如果子类A继承了父类B,不论 是直接或间接的继承,子类A都必将依赖父类B。子类A必须使用在存在父类B的环境中, 父类B不存在子类A就不能使用,这样将影响子类A的可移植性。一旦父类B发生任何变 更,更改或去掉一个函数名,或者改变一个函数的参数,都将导致子类A不得不变更,甚 至重写。假如父类B的子类数十上百个,甚至贯穿这个项目各个模块,这样的变更是灾难 性的。这种情况最典型的例子是我们现在使用hibernate和spring设计DAO对象的方式,具 体的描述参见我写的如何在struts + spring + hibe

23、rnate的框架下构建低耦合高内聚的软件 结构一文。总之,“低耦合”给软件项目带来的优点是:易于变更、易于重用一个优秀软件开发人员的必修课:高内聚高内聚Java软件工程软件模式一个重要的模式:高内聚。高内聚(High Cohesion)高内聚是另一个普遍用来评判软件设计质量的标准。内聚,更为专业的说法叫功能内聚,是 对软件系统中元素职责相关性和集中度的度量。如果元素具有高度相关的职责,除了这些职 责内的任务,没有其它过多的工作,那么该元素就具有高内聚性,反之则为低内聚性。高内 聚要求软件系统中的各个元素具有较高的协作性,因为在我们在完成软件需求中的一个功 能,可能需要做各种事情,但是具有高内聚

24、性的一个元素,只完成它职责内的事情,而把那 些不在它职责内的事情拿去请求别人来完成。这就好像,如果我是一个项目经理,我的职责 是监控和协调我的项目各个阶段的工作。当我的项目进入需求分析阶段,我会请求需求分析 员来完成;当我的项目进入开发阶段,我会请求软件开发人员来完成;当我的项目需要测试 的时候,我会请求测试人员。如果我参与了开发,我就不是一个高内聚的元素,因为 开发不是我的职责。我们的项目为什么要高内聚呢?我觉得可以从可读性、复用性、可维护 性和易变更性四个方面来理解。可读性一个人写文章、讲事情,条理清晰才能易于理解,这同样发生在读写软件代码上。如果一堆 代码写得一团乱麻,东一个跳转西一个调

25、用,读它的人会感觉非常头疼。这种事情也许一直 在写程序的你我都曾经有过经历。如果一段程序条理非常清晰,每个类通过名称或说明都能 清楚明白它的意义,类的每个属性、函数也都是易于理解的它所应当完成的任务和行为,这 段程序的可读性必然提高。在软件产业越来越密集,软件产业中开发人员协作越来越紧密、 分工越来越细的今天,软件可读性的要求相信也越来越为人们所重视。复用性 在软件开发中,最低等级的复用是代码拷贝,然后是函数的复用、对象的复用、组件的复用。 软件开发中最懒的人是最聪明的人,他们总是想到复用。在代码编写的时候突然发现某个功 能是曾经实现过的功能,直接把它拷贝过来就ok 了。如果这段代码在同一个对

26、象中,那么 就提出来写一个函数到处调用就行了。如果不是在同一个对象中呢,就将其抽象成一个对象 到处调用吧。如果不在一个项目中呢,那就做成组件给各个项目引用吧。代码复用也使我们 的代码在复用的过程中不断精化、不断健壮、提高代码质量。代码的复用的确给我们的开发 带来了不少便利,但是一段代码能否在各个需要的地方都能复用呢?这给我们的软件开发质 量提出了新的要求:好的代码可以复用,不好的则不行。软件中的一个对象如果能保证能完 成自己职能范围内的各项任务,同时又不去理会与自己职能无关的其它任务,那么它就能够 保证功能的相对独立性,也就可以脱离自己所处的环境而复用到其它环境中,这是一个具有 内聚性的对象。

27、可维护性和易变更性在前面如何在struts+spring+hibernate的框架下构建低耦合高内聚的软件中我提到,我们 现在的软件是在不断变更的,这种变更不仅来自于我们的客户,更来自于我们的市场。如果 我们的软件通过变更能及时适应我们的市场需求,我们就可以在市场竞争中获胜。如何能及 时变更以适应我们的市场呢,就是通过调整软件的结构,使我们每次的变更付出的代价最小, 耗费的人力最小,这种变更才最快最经济。高内聚的软件,每个系统、模块、类的任务都高 度相关,就使每一次的变更涉及的范围缩小到最小。比如评审表发生了变更,只会与评审表 对象有关,我们不会去更改其它的对象。如果我们能做到这一点,我们的系

28、统当然是可维护 性好、易变更性好的系统。那么,我们如何做到高内聚呢?就拿前面我提到的评审项目举例。我现在要为“评审表”对 象编写一段填写并保存评审表的代码。评审表对象的职责是更新和查询评审表的数据,但是 在显示一个要填写的评审表的时候,我需要显示该评审计划的名称、该评审计划有哪些评审 对象需要评审。现在我如何编写显示一个要填写的评审表的代码?我在评审表对象的这个相 应的函数中编写一段查询评审计划和评审对象的代码吗?假如你这样做了,你的代码就不是 高内聚的,因为查询评审计划和评审对象的数据不是它的职责。正确的方法应当去请求“评 审计划”对象和“评审对象”对象来完成这些工作,而“评审表”对象只是获

29、取其结果。另外,如果一个对象要完成一个虽然在自己职责范围内,但过程非常复杂的任务时,也应当 将该任务分解成数个功能相对独立的子函数来完成。我曾经看见一个朋友写的数百行的一个 函数,让人读起来非常费劲。同时这样的函数中一些相对独立的代码,本可以复用到其它代 码中,也变成了不可能。所以我给大家的建议是,不要写太长的函数,超过一百行就可以考 虑将一些功能分解出去。与“低耦合”一样,高内聚也不是一个绝对,而是一个相对的指标,应当适当而不能过度。 正如我们在现实生活中,如果在一个十来人的小公司,每个人的分工可能会粗一些,所分配 的职责会广一些杂一些,因为其总体的任务少;而如果在一个一两百人的大公司,每个

30、人的 分工会细一些,所分配的任务会更加专一些,因为总体任务多,更需要专业化的分工来提高 效率。软件开发也是一样,如果“评审计划”对象完成的业务功能少,并且不复杂,它完全 可以代理它的子表“评审对象”和“评审者”的管理。但是“评审计划”对象需要完成的“对 评审计划表的管理”这个基本职责包含的业务功能繁多或者复杂,它就应当将“对评审对象 表的管理”交给“评审对象”对象,将“对评审者表的管理”交给“评审者”对象。同样, 高内聚的可维护性好、易变更性好只能是一个相对的指标。如果一个变更的确是大范围的变 更,你永远不可能通过内聚就不进行大范围的变更了。同时内聚也是要付出代价的,所以你 也不必要去为了一个

31、不太可能的变更去进行过度设计,应当掌握一个度。过度的内聚必将增 加系统中元素之间的依赖,提高耦合度。所以“高内聚”与“低耦合”是矛盾的,必须权衡 利弊,综合地去处理。在李洋等人翻译的UML和模式应用中,将内聚和耦合翻译为软 件工程中的阴与阳,是中国人对内聚和耦合的最佳解释。综上所述,“高内聚”给软件项目带来的优点是:可读性强、易维护和变更、支持低耦合、 移植和重用性强。当我们分析清楚客户需求设计出用例模型以后,当我们分析清楚客户的业务环境制 作出领域模型以后,当我们综合用例模型、领域模型和我们的聪明才智设计出一个又一个的 类和它们各自的方法以后,当就在一切都准备就绪只欠东风的关键时刻,一个对象

32、发出了 撕心裂肺的怒吼一一谁来创建我? ! 一个对象,不管拥有多么强大的功能,不管进行了 多么精巧的设计,如果不能被创建,就如同韩信不能做将军,孙膑不能当军师,勾践不能回 越国,刘备不能得荆州,一切一切的雄才武略都如废纸一张。既然“创建”对于对象如此重 要,我们就来好好探讨一下GRASP中关于对象创建的问题。3.创建者(Creator)当我们完成了用例模型、领域模型、对象分析的设计,初步完成了对象设计和职责 分配的工作,开始进一步细化的时候,一个我们不得不考虑的问题就摆在我们的面前 谁来创建这些对象?也许现在的你会觉得好笑,这也是问题吗?在软件实际开发过程中,谁 需要使用某个对象,就去创建它就

33、行了,有什么好讨论的。但是,我不得不说的是,如果 你只是漫不经心地想要随意开发一套软件系统,仅仅是完成自己工作而已,你完全不用考虑 创建对象的问题。然而如果你希望开发一套高质量的、低耦合的、封装性和复用性高的软 件系统,你必须得认真考虑这个问题。为什么呢?因为系统中如果一个对象A创建另一个 对象B,那么对象A就必将与对象B耦合,这个我已经在前面(原创)一个优秀的软件开 发人员的必修课:GRASP(2)低耦合中提到。我们可以想像,如果在你的系统中,对于 对象B,你也去创建,我也去创建,大家都去创建,对象B势必与许多对象发生耦合,耦 合度将大大提高;但如果对象B可以都由对象A来创建,然后由对象A向

34、其它需要对象B 的对象提供对象B,即其它对象需要使用对象B的时候都向对象A索要,那么整个系统对 对象B的耦合将会大大降低,同时对象A和B也可以形成一个封装的、可复用的独立系统, 则这个软件系统的设计质量势必提高。所以,对象创建的问题不可不察。那么为了降低系统耦合,提高系统的清晰度、封装性和可复用性,应该有一些通用的 原则,以用于对象职责分配中,关于“创建对象”这类职责的分配。这些原则的描述就在 GRASP的“创建者”模式中。1)创建者模式的描述如果以下条件之一为真(越多越好),则将创建类A的实例的职责分配给类B (B是 对象A的创建者):B包含或组成聚集A。B记录A。B直接使用A。B具有A的初

35、始化数据,并且在创建A时会将这些数据传递给A。因此对于A的 创建而言,B是信息专家(关于“信息专家”模式我会在后面描述)。如果有以上多个选项适用,通常首先条件1 (B包含或组成聚集A)。2)何时使用在理解创建者模式的时候,我认为一个首先必须理解的问题是,在软件项目的整个过 程中,它应该是在什么阶段使用。一个网友曾经发帖问我,他不清楚GRASP一般适应于软 件开发的什么阶段。我认为,GRASP作为职责驱动的基本原则,一般适用于对象分析和设 计的中前期。在软件项目的前期,也就是需求分析阶段,我们通常是制作用例模型和领域模 型。用例模型往往描述的是用户对整个项目提出的所有功能的集合。对于每个功能,我

36、们 通常使用用例,并在用例描述中描述该用例的主要流程。因此用例模型描述的通常是需求分 析中动 态的部分。领域模型往往描述的是用户提出的整个问题空间中的各种事物及它们的 相互关系,因此领域模型描述的是需求分析中静态部分。领域模型虽然不是完全,但却是 以此为基础,形成软件系统中的软件类。为什么不是“完全”呢?因为软件系统中需要什么类, 是软件系统功能的要求。假如在领域模型中的某个对象,它的确是用户问题空间中的事物, 但是它在软件系统功能的要求中使用不到它,那么在软件系统中它同样不能成为一个软件 类。当我们设计好了软件类以后,我们就将根据用例模型,为所有的软件类分配职责,确 定它们各自应具有的行为及

37、相互的协作。每个软件类应当如何分配它们的职责,也就是说用 例模型中描述的各个功能应当交给哪个或哪些软件类去实现,这个工作就是对软件类的职 责分配。完成这个工作的阶段主要在对象分析的中前期,也正是我们大量运用GRASP的时 期。职责分配需要一定的原则,这个原则将是GRASP“信息专家”模式将要讨论的内容。当 我们将一个一个的软件类的职责分配好了,其各自的行为和属性也确定下来了,下一步需要 考虑的问题就是它们应当在何时,由谁来创建。解决对象创建问题当然应当交给创建者模 式来完成。因此不难理解,创建者模式应当运用在对象分析中前期稍靠后一点儿的阶段,即 软件类的主要职责及其行为都设计好了,讨论该何时,

38、由谁来创建它的时候。3)为什么我们做事往往有个习惯,凡事问个为什么。前面我提到,使用创建者模式的主要目的 是可以降低系统的耦合。那么,我们在使用创建者模式的这几个建议的时候是如何降低耦合 的呢?这一直是困扰了我很久的一个疑问,C raig Larman对于这一点没有清楚地描述。但是, 我们接受一个新事物,如果都没有弄清楚为什么就盲目接受,这是一种十分不严谨的态度。 我现在通过我在项目中的一些实践和自己的一点儿愚悟,谈谈自己的看法。创建者模式告诉我们,如果系统中存在包含者容纳被包含者,或整体聚集部分,则 包含者往往是被包含者的最佳创建者,整体往往是部分的最佳创建者。为什么呢?首先, 这样的设计易

39、于理解,可读性强。为什么这么说呢,我们用我们常见的单据与单据明细来说 明吧。一张单据有多条单据明细,这些单据明细聚集于单据中,是单据的一个部分。对于 某张单据,我们只有去填写这张单据,才会去填写它的明细。同样,我们要查看和修改这张 单据的明细,首先肯定是找到这张单据。以上这些是我们在实际生活中大家都认同的管理 单据的方式。GRASP所提倡的一个十分重要观念就是低表示差异,也就是说实际生活中是 怎样的,我们就怎样设计。用更加专业点儿的术语表述为:软件设计应当与用户的问题空间 保持低表示差异。正因为如此,我的软件设计中,一个单据对象存在了,它的单据明细对 象才可以存在;要得到一个单据明细对象,应当

40、先找到它所在的单据对象。既然单据对象与 单据明细对象是如此的逻辑关系,我们假设单据明细对象不是由单据对象创建,而是另一 个对象X,那么对象X即要创建单据对象,又要创建单据明细对象,还要维持单据对象与 单据明细对象的聚集关系。这样的设计不难看出,代码实现比较复杂,可读性差。不仅如此, 其耦合度也必然增加。对象X与单据对象和单据明细对象都需要耦合,单据对象与单据明 细对象之间同样需要耦合。如果修改一下设计,对象X创建单据对象,而单据对象去创建 单据明细对象,则对象X只与单据对象耦合,单据对象再与单据明细对象耦合,耦合度就 降低了。所以,包含者创建被包含者,整体创建部分可以有效降低耦合。同时,这样的

41、设计, 单据明细对象的创建只与单据对象有关,整个系统都由单据对象向其它对象提供单据明细 对象,那么单据对象与单据明细对象则可以比较容易地形成一个关于单据的独立系统。这 样一个独立系统可以比较便利地应用到其它需要使用单据的地方,其可移植性也就提高了。尽管包含者往往是被包含者的最佳创建者,整体往往是部分的最佳创建者,但是在一 个软件系统中,并不是所有类都有它的包含者或者整体。如果没有,谁应当创建它呢?记录 者当然是另一个可以考虑的人选。仓库管理员管理进出库是ERP系统一个非常重要模块。 在实际生活中,一批产品存入仓库,仓库管理员当然是需要填写入库单。这个入库单在仓库 管理员填写之前,本没有,是仓库

42、管理员填写之后才有,我们是不是可以说仓库管理员创 建了一个入库单。既然现实生活中如此,我们在软件设计中是不是也应该由仓库管理员对象 负责创建入库单对象,符合低表示差异,不言而喻也符合低耦合。同样,在这个软件系统 中仓库管理员填写入库单,在其它的系统中也同样是仓库管理员填写入库单。仓库管理员与 入库单这对封装的独立体也同样可以应用到别的系统,可移植性和封装性也得以提高。因 此记录者创建记录内容也是我们可以考虑的一个方案。如果我们正在设计的软件类也没有记录者,这可如何是好?具有创建这个类所需数据 的那个类可以考虑,那个类就是信息专家(什么是“信息专家”,我会在以后对信息专家模式 的文章中详细描述)

43、。在我们的设计过程中,很多类的创建是需要一些初始化数据的。最典 型的就是我们的vo (值对象)。在java程序中,vo往往是用来传输数据的,也就是说创建 vo的初始化数据就是这些它需要传递的数据。如果这些数据在某个Action中,创建vo的当 然就是这个Action。而如果这个vo的初始化数据来自BUS,则该vo的创建当然应当是这 个BUS中。如果以上方法还不行,那我们就只有找使用者了。寻找使用者是我们创建类最常用 的一种方法,但它的缺点也非常明显。正如前面我描述的,我们系统中对某个软件类的使 用可能分布到系统的各个角落。当我们因为某个需求需要修改这个类的时候,我们根本不知 道谁在使用它。正因

44、为如此,这样的修改变得如梦魇一般,不断地搜索,不断地修改。我 们前面说过,合理的软件构造是为了使我们的变更代价最小,而这样的变更将使我们的代价 太大了,也许一个不经意的变更错误将造成我们的系统中一个意想不到的地方发生异常。 故我们变更后测试的代价也就因此而增大。总之,寻找使用者作为创建者是我们业务分析阶 段最后的终极选择。4)创建者模式是原则,不是准则“创 建者模式是原则,不是准则难道“原则”和“准则”还有不同吗?当然。创建者模式 是原则,所以我们在业务分析阶段应当尽量遵守。但创建者模式不是准则,因为并非我们 的所有软件类都必须遵守。为什么这么说呢?随着项目的进行,我们的分析设计就不再停留 在

45、业务的分析上,各种具体的框架和技术将不断引进项目中,这时对象的创建就不一定符 合创建者模式。比如,为了提高系统的性能和可维护性、更好地处理对象的创建与回收等复 杂的问题,我们常常把对象的创建交给工厂,如spring的beanFactory、hibernate的sessionFactory等等。“工厂”不论是“具体工厂”还是“抽象工厂”,都不符合创建者模式中的 任何一个条件。为什么呢?因为创建者模式中的各个条件都是来自对领域模型和设计模型 的分析,说得更加直白一点儿就是对客户现实世界的分析,与技术无关。在技术领域的对象 分析和设计已经超出了创建者模式适用的范围,这更多的是出现在对象分析和设计的中后 期。所以,正如我前面所述的,创建者模式适用的时期,是对象分析和设计的中前期,对象 的业务分析稍晚一点儿的阶段。总之,合理地创建对象可以有效的提供可读性、降低耦合度、提高系统的封装性和可 移植性,我们应当引起重视。模块的耦合与内聚DELPHI 技术 2009-08-05 00:07 阅读 52 评论 0字号:大中小 模块的耦合与内聚 耦合(C oupling)是模块之间依赖程度的度量。内聚和耦合是密切相关的,与其它模块存在强耦 合的模块通常意味着弱内聚,而强内聚的模块通常意味着与其它模块之间存在弱耦合。模块设计追求强内 聚,弱耦合。内聚(Cohesion)

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论