`

如何提高代码质量三 可变更性

    博客分类:
  • Java
阅读更多

c. 适配器模式
  我的笔记本是港货,它的插头与我们常用的插座不一样,所有我出差的时候我必须带一个适配器,才能使用不同地方的插座。这是一个对适配器模式最经典的描述。当我们设计的系统要与其它系统交互,或者我们设计的模块要与其它模块交互时,这种交互可能是调用一个接口,或者交换一段数据,接受方常常因发送方对协议的变更而频繁变更。这种变更,可能是接受方来源的变更,比如原来是A系统,现在变成B系统了;也可能是接受方自身的代码变更,如原来的接口现在增加了一个参数。由于发送方的变更常常导致接受方代码的不稳定,即频繁跟着修改,为接受方的维护带来困难。
  遇到这样的问题,一个有经验的程序员马上想到的就是采用适配器模式。在设计时,我方的接口按照某个协议编写,并且保持固定不变。然后,在与真正对方接口时,在前段设计一个适配器类,一旦对方协议发生变更,我可以换个适配器,将新协议转换成原协议,问题就解决了。适配器模式应当包含一个接口和它的实现类。接口应当包含一个本系统要调用的方法,而它的实现类分别是与A系统接口的适配器、与B系统接口的适配器...

我曾经在一个项目中需要与另一个系统接口,起初那个系统通过一个数据集的方式为我提供数据,我写了一个接收数据集的适配器;后来改为用一个XML数据流的形式,我又写了一个接收XML的适配器。虽然为我提供数据的方式不同,但是经过适配器转换后,输出的数据是一样的。通过在spring中的配置,我可以灵活地切换到底是使用哪个适配器。
  d. 模板模式

  32个经典模式中的模板模式,对开发者的代码规划能力提出了更高的要求,它要求开发者对自己开发的所有代码有一个相互联系和从中抽象的能力,从各个不同的模块和各个不同的功能中,抽象出其过程比较一致的通用流程,最终形成模板。譬如说,读取XML并形成工厂,是许多模块常常要使用的功能。它们虽然有各自的不同,但是总体流程都是一样的:读取XML文件、解析XML数据流、形成工厂。正因为有这样的特征,它们可以使用共同的模板,那么,什么是模板模式呢?
  模板模式(Template Model)通常有一个抽象类。在这个抽象类中,通常有一个主函数,按照一定地顺序去调用其它函数。而其它函数往往是某这个连续过程中的各个步骤,如以上实例中的读取XML文件、解析XML数据流、形成工厂等步骤。由于这是一个抽象类,这些步骤函数可以是抽象函数。抽象类仅仅定义了整个过程的执行顺序,以及一些可以通用的步骤(如读取XML文件和解析XML数据流),而另一些比较个性的步骤,则由它的继承类自己去完成(如上例中的“形成工厂”,由于各个工厂各不一样,因此由各自的继承类自己去决定它的工厂是怎样形成的)。

各个继承类可以根据自己的需要,通过重载重新定义各个步骤函数。但是,模板模式要求不能重载主函数,因此正规的模板模式其主函数应当是final(虽然我们常常不这么写)。另外,模板模式还允许你定义的这个步骤中,有些步骤是可选步骤。对与可选步骤,我们通常称为“钩子(hood)”。它在编写时,在抽象类中并不是一个抽象函数,但却是一个什么都不写的空函数。继承类在编写时,如果需要这个步骤则重载这个函数,否则就什么也不写,进而在执行的时候也如同什么都没有执行。
  通过以上对模板模式的描述可以发现,模板模式可以大大地提高我们的代码复用程度。
  以上一些常用设计模式,都能使我们快速提高代码质量。还是那句话,设计模式不是什么高深的东西,恰恰相反,它是初学者快速提高的捷径。然而,如果说提高代码复用是提高代码质量的初阶,使用设计模式也只能是提高代码质量的中阶。那么,什么是高阶呢?我认为是那些分析设计理论,更具体地说,就是职责驱动设计和领域驱动设计。

3)职责驱动设计和领域驱动设计
  前面我提到,当我们尝试写一些复杂功能的时候,我们把功能分解成一个个相对独立的函数。但是,应当将这些函数分配到哪个类中呢?也就是系统中的所有类都应当拥有哪些函数呢?或者说应当表现出哪些行为呢?答案就在这里:以职责为中心,根据职责分配行为。我们在分析系统时,首先是根据客户需求进行用例分析,然后根据用例绘制领域模式和分析模型,整个系统最主要的类就形成了。通过以上分析形成的类,往往和现实世界的对象是对应的。正因为如此,软件世界的这些类也具有了与现实世界的对象相对应的职责,以及在这些职责范围内的行为。
  职责驱动设计(Responsibility Drive Design,RDD)是Craig Larman在他的经典著作《UML和模式应用》中提出的。职责驱动设计的核心思想,就是我们在对一个系统进行分析设计的时候,应当以职责为中心,根据职责分配行为。这种思想首先要求我们设计的所有软件世界的对象,应当与现实世界尽量保持一致,他称之为“低表示差异”。有了低表示差异,一方面提高了代码的可读性,另一方面,当业务发生变更的时候,也可以根据实际情况快速应对变更。
  Craig Larman在提出职责驱动设计理论的同时,还提出了GRASP设计模式,来丰富这个理论。在GRASP设计模式中,我认为,低耦合、高内聚、信息专家模式最有用。
  继Craig Larman提出的职责驱动设计数年之后,另一位大师提出了领域驱动设计。领域驱动设计(Domain Drive Design,DDD)是Eric Evans在他的同名著作《领域驱动设计》中提出的。在之前的设计理论中,领域模型是从用例模型到分析模型之间的一种中间模型,也就是从需求分析到软件开发之间的一种中间模型。这么一个中间模型,既不是需求阶段的重要产物,在开发阶段也不以它作为标准进行开发,仅仅是作为参考,甚至给人感觉有一些多余。但是,Evans在领域驱动设计中,将它放到了一个无比重要的位置。按照领域驱动设计的理论,在需求分析阶段,需求分析人员使用领域模型与客户进行沟通;在设计开发阶段,开发人员使用领域模型指导设计开发;在运行维护和二次开发阶段,维护和二次开发人员使用领域模型理解和熟悉系统,并指导他们进行维护和二次开发。总之,在整个软件开发的生命周期中,领域模型都成为了最核心的内容。
  领域驱动设计继承了职责驱动设计。在领域驱动设计中强调的,依然是低表示差异,以及职责的分配。但是,如何做到低表示差异呢?如何完成职责分配呢?领域驱动设计给了我们完美的答案,那就是建立领域模型。领域驱动设计改变了我们的设计方式。在需求分析阶段,用例模型已不再是这个阶段的核心,而是建立领域模型。在开发和二次开发阶段,开发人员也不再是一埋头地猛扎进程序堆里开始编程,而是首先细致地进行领域模型分析。领域驱动设计强调持续精化,使领域模型不再是一旦完成分析就扔在一边不再理会的图纸,而是在不断理解业务的基础上不断修改和精化领域模型,进而驱动我们代码的精化。领域驱动设计强调的不再是一次软件开发过程中我们要做的工作,它看得更加长远,它强调的是一套软件在相当长一段时间内持续升级的过程中我们应当做的工作。我认为,领域驱动设计是提高代码质量的最高等级。当时,使用领域驱动设计进行软件开发是一场相当巨大的改革,它颠覆了我们过去的所有开发模式,我们必须脚踏实地地一步一步去实践和改变。

职责驱动设计
  随着软件业的不断发展,随着软件需求的不断扩大,软件所管理的范围也在不断拓宽。过去一个软件仅仅管理一台电脑的一个小小的功能,而现在被扩展到了一个企业、一个行业、一个产业链。过去我们开发一套软件,只有少量的二次开发,当它使用到一定时候我们就抛弃掉重新又开发一套。现在,随着用户对软件依赖程度的不断加大,我们很难说抛弃一套软件重新开发了,更多的是在一套软件中持续改进,使这套软件的生命周期持续数年以及数个版本。正是因为软件业面临着如此巨大的压力,我们的代码质量,我们开发的软件拥有的可变更性和持续改进的能力,成为软件制胜的关键因素,令我们不能不反思。
  代码质量评价的关键指标:低耦合,高内聚
  耦合就是对某元素与其它元素之间的连接、感知和依赖的量度。耦合包括:
  1.元素B是元素A的属性,或者元素A引用了元素B的实例(这包括元素A调用的某个方法,其参数中包含元素B)。
  2.元素A调用了元素B的方法。
  3.元素A直接或间接成为元素B的子类。
  4.元素A是接口B的实现。
  如果一个元素过于依赖其它元素,一旦它所依赖的元素不存在,或者发生变更,则该元素将不能再正常运行,或者不得不相应地进行变更。因此,耦合将大大影响代码的通用性和可变更性。
  内聚,更为专业的说法叫功能内聚,是对软件系统中元素职责相关性和集中度的度量。如果元素具有高度相关的职责,除了这些职责内的任务,没有其它过多的工作,那么该元素就具有高内聚性,反之则为低内聚性。内聚就像一个专横的管理者,它只做自己职责范围内的事,而将其它与它相关的事情,分配给别人去做。
  高质量的代码要求我们的代码保持低耦合、高内聚。但是,这个要求是如此的抽象与模糊,如何才能做到这些呢?软件大师们告诉我们了许多方法,其中之一就是Craig Larman的职责驱动设计。
  职责驱动设计(Responsibility Drive Design,RDD)是Craig Larman在他的经典著作《UML和模式应用》中提出的。要理解职责驱动设计,我们首先要理解“低表示差异”。
  低表示差异
  我们开发的应用软件实际上是对现实世界的模拟,因此,软件世界与现实世界存在着必然的联系。当我们在进行需求分析的时候,需求分析员实际上是从客户那里在了解现实世界事物的规则、工作的流程。如果我们在软件分析和设计的过程中,将软件世界与现实世界紧密地联系到一起,我们的软件将更加本色地还原事物最本质的规律。这样的设计,就称之为“低表示差异”。


采用“低表示差异”进行软件设计,现实世界有什么事物,就映射为软件世界的各种对象(类);现实世界的事物拥有什么样的职责,在软件世界里的对象就拥有什么样的职责;在现实世界中的事物,因为它的职责而产生的行为,在软件世界中就反映为对象所拥有的函数。
  低表示差异,使分析设计者对软件的分析和设计更加简单,思路更加清晰;使代码更加可读,阅读者更加易于理解;更重要的是,当需求发生变更,或者业务产生扩展时,设计者只需要遵循事物本来的面貌去思考和修改软件,使软件更加易于变更和扩展。

角色、职责、协作
  理解了“低表示差异”,现在我们来看看我们应当如何运用职责驱动设计进行分析和设计。首先,我们通过与客户的沟通和对业务需求的了解,从中提取出现实世界中的关键事物以及相互之间的关系。这个过程我们通常通过建立领域模型来完成。领域模型建立起来以后,通过诸如Rational Rose这样的设计软件的正向工程,生成了我们在软件系统中最初始的软件类。这些软件类,由于每个都扮演着现实世界中的一个具体的角色,因而赋予了各自的职责。前面我已经提到,如果你的系统采用职责驱动设计的思想进行设计开发,作为一个好的习惯,你应当在每一个软件类的注释首行,清楚地描述该软件类的职责。
  当我们完成了系统中软件类的制订,分配好了各自的职责,我们就应该开始根据软件需求,编写各个软件类的功能。在前面我给大家提出了一个建议,就是不要在一个函数中编写大段的代码。编写大段的代码,通常会降低代码的内聚度,因为这些代码中将包含不是该软件类应当完成的工作。作为一个有经验的开发人员,在编写一个功能时,首先应当对功能进行分解。一段稍微复杂的功能,通常都可以被分解成一个个相对独立的步骤。步骤与步骤之间存在着交互,那就是数据的输入输出。通过以上的分解,每一个步骤将形成一个独立的函数,并且使用一个可以表明这个步骤意图的释义函数名。接下来,我们应当考虑的,就是应当将这些函数交给谁。它们有可能交给原软件类,也有可能交给其它软件类,其分配的原则是什么呢?答案是否清楚,那就是职责。每个软件类代表现实世界的一个事物,或者说一个角色。在现实世界中这个任务应当由谁来完成,那么在软件世界中,这个函数就应当分配给相应的那个软件类。
  通过以上步骤的分解,一个功能就分配给了多个软件类,相互协作地完成这个功能。这样的分析和设计,其代码一定是高内聚的和高可读性的。同时,当需求发生变更的时候,设计者通过对现实世界的理解,可以非常轻松地找到那个需要修改的软件类,而不会影响其它类,因而也就变得易维护、易变更和低耦合了。
  说了这么多,举一个实例也许更能帮助理解。拿一个员工工资系统来说吧。当人力资源在发放一个月工资的时候,以及离职的员工肯定不能再发放工资了。在系统设计的期初,开发人员商量好,在员工信息中设定一个“离职标志”字段。编写工资发放的开发人员通过查询,将“离职标志”为false的员工查询出来,并为他们计算和发放工资。但是,随着这个系统的不断使用,编写员工管理的开发人员发现,“离职标志”字段已经不能满足客户的需求,因而将“离职标志”字段废弃,并增加了一个“离职时间”字段来管理离职的员工。然而,编写工资发放的开发人员并不知道这样的变更,依然使用着“离职标志”字段。显然,这样的结果就是,软件系统开始对离职员工发放工资了。仔细分析这个问题的原因,我们不难发现,确认员工是否离职,并不是“发放工资”软件类应当完成的工作,而应当是“员工管理”软件类应当完成的。如果将“获取非离职员工”的任务交给“员工管理”软件类,而“发放工资”软件类仅仅只是去调用,那么离职功能由“离职标志”字段改为了“离职时间”字段,其实就与“发放工资”软件类毫无关系。而作为“员工管理”的开发人员,一旦发生这样的变更,他当然知道去修改自己相应的“获取非离职员工”函数,这样就不会发生以上问题。通过这样一个实例,也许你能够理解“职责驱动设计”的精要与作用了吧。

职责分配与信息专家
  通过以上对职责驱动设计的讲述,我们不难发现,职责驱动设计的精要就是职责分配。但是,在纷繁复杂的软件设计中,如何进行职责分配常常令我们迷惑。幸运的是,Larman大师清楚地认识到了这一点。在他的著作中,信息专家模式为我们提供了帮助。
  信息专家模式(又称为专家模式)告诉我们,在分析设计中,应当将职责分配给软件系统中的这样一个软件类,它拥有实现这个职责所必须的信息。我们称这个软件类,叫“信息专家”。用更加简短的话说,就是将职责分配给信息专家。
  为什么我们要将职责分配给信息专家呢?我们用上面的例子来说明吧。当“发放工资”软件类需要获取非离职员工时,“员工管理”软件类就是“获取非离职员工”任务的信息专家,因为它掌握着所有员工的信息。假设我们不将“获取非离职员工”的任务交给“员工管理”软件类,而是另一个软件类X,那么,为了获取员工信息,软件类X不得不访问“员工管理”软件类,从而使“发放工资”与X耦合,X又与“员工管理”耦合。这样的设计,不如直接将“获取非离职员工”的任务交给“员工管理”软件类,使得“发放工资”仅仅与“员工管理”耦合,从而有效地降低了系统的整体耦合度。
  总之,采用“职责驱动设计”的思路,为我们提高软件开发质量、可读性、可维护性,以及保持软件的持续发展,提供了一个广阔的空间。
  • 大小: 16.7 KB
  • 大小: 16.7 KB
  • 大小: 16.7 KB
分享到:
评论

相关推荐

    如何提高代码质量

    如何提高代码质量,相信不仅是在座所有人苦恼的事情,也是所有软件项目苦恼的事情。...我们评价高质量代码有三要素:可读性、可维护性、可变更性。我们的代码要一个都不能少地达到了这三要素的要求才能算高质量的代码。

    高质量代码有三要素:可读性、可维护性、可变更性

    我们评价高质量代码有三要素:可读性、可维护性、可变更性。我们的代码要一个都不能少地达到了这三要素的要求才能算高质量的代码。一提到可读性似乎有一些老生常谈的味道,但令人沮丧的是,虽然大家一而再,再而三地...

    一堂如何提高代码质量的课(一)

    高质量代码的三要素我们评价高质量代码有三要素:可读性、可维护性、可变更性。我们的代码要一个都不能少地达到了这三要素的要求才能算高质量的代码。1.可读性强一提到可读性似乎有一些老生常谈的味道,但令人沮丧的...

    一堂如何提高代码质量的培训课(二)

    由于软件合理地设计,修改所付出的代价越小,则软件的可变更性越好,即代码设计的质量越高。一种非常理想的状态是,无论客户需求怎样变化,软件只需进行适当地修改就能够适应。但这之所以称之为理想状态,因为客户...

    GitHub,代码管理的黄金标准!.zip

    这些功能可以进一步提高开发效率和代码质量。 使用GitHub非常简单。首先,需要注册一个GitHub账户,并创建一个新的仓库。然后,将代码推送到GitHub仓库中,并与其他开发者协作。 GitHub适用于多种实际应用场景,如...

    ToadforSQLServer_6.8.2.9.x64

    Toad for SQL Server(注意:资源为64位,提供注册码)是一款专为sql sever数据库打造的功能强大的开发工具,内置sql编辑器、查询生成器、项目...轻松解决问题、管理变更并提高代码质量、性能和可维护性的最高水平。

    毕业设计代码包(代码)【煤炭进销存管理系统——JAVA】

    仓库管理子系统用于管理煤炭的库存状况等基本信息,完成每次煤炭进出仓库的质量检验及数据变更,能够对不同品种进行分类管理。销售管理子系统用于处理煤炭销售的日常业务,完成销售订单、出库申请单等基本文件的编制...

    毕业设计-基于JAVA的springboot家政服务管理平台-2(源代码+说明)

    本系统是为了顺应了时代的变更、家政服务行业的迅猛发展而设计的,利用互联网的交互性和实时性,为用户提供便利、快捷、舒适的服务,提高服务质量,让用户随时了解系统动态,从而提高维修体验,而管理者也可以轻松...

    毕业设计-基于JAVA的springboot家政服务管理平台-1(源代码+说明)

    本系统是为了顺应了时代的变更、家政服务行业的迅猛发展而设计的,利用互联网的交互性和实时性,为用户提供便利、快捷、舒适的服务,提高服务质量,让用户随时了解系统动态,从而提高维修体验,而管理者也可以轻松...

    软件工程知识点

    (1)危机现象:软件开发成本与进度估计不准确,软件产品与用户要求不一致,软件产品质量可靠性差,软件文档不完整不一致,软件产品可维护性差,软件生产率低。 (2)危机原因:软件的不可见性,系统规模庞大,生产...

    毕业设计:留守儿童爱心网站

    本系统是为了顺应了时代的变更、留守儿童爱心行业的迅猛发展而设计的,利用互联网的交互性和实时性,为用户提供便利、快捷、舒适的服务,提高服务质量,让用户随时了解系统动态,从而提高维修体验,而管理者也可以...

    毕业设计-基于JAVA的springboot留守儿童爱心网站-2(源代码+说明)

    本系统是为了顺应了时代的变更、留守儿童爱心行业的迅猛发展而设计的,利用互联网的交互性和实时性,为用户提供便利、快捷、舒适的服务,提高服务质量,让用户随时了解系统动态,从而提高维修体验,而管理者也可以...

    毕业设计-基于JAVA的springboot留守儿童爱心网站-1(源代码+说明)

    本系统是为了顺应了时代的变更、留守儿童爱心行业的迅猛发展而设计的,利用互联网的交互性和实时性,为用户提供便利、快捷、舒适的服务,提高服务质量,让用户随时了解系统动态,从而提高维修体验,而管理者也可以...

    软件工程-理论与实践(许家珆)习题答案

     硬件特征:速度、容量及工作可靠性有明显提高,价格降低,销售有爆炸性增长 。  软件特征:程序员数量猛增,开发人员素质低。  ③ 软件工程阶段。  硬件特征:向超高速、大容量、微型化及网络化方向发展。  ...

    BMC BladeLogic加快Virgin Media上市脚步

    在目前的金融危机下,与任何企业一样,Virgin Media需要控制其数据中心的...通过自动执行打包和代码升级过程,把应用版本和更新从开发环境部署到生产环境中,该公司加快了上市速度,缩短了发布周期,并提高了应用质量。

    宾馆管理信息系统课程设计.doc

    第二节:新系统数据流程图 第三节:数据字典 第四章:系统实施 第一节:新系统功能设计 第二节:代码设计 第三节:数据库设计 第四节:输入输出设计 小结: 致谢: 参考文献 管理信息系统 课程设计任务书 一、课程...

Global site tag (gtag.js) - Google Analytics