当前位置:文档之家› 高质量代码三要素

高质量代码三要素

高质量代码的三要素我们评价高质量代码有三要素:可读性、可维护性、可变更性。

我们的代码要一个都不能少地达到了这三要素的要求才能算高质量的代码。

1.可读性强一提到可读性似乎有一些老生常谈的味道,但令人沮丧的是,虽然大家一而再,再而三地强调可读性,但我们的代码在可读性方面依然做得比较糟糕。

每当我看到大段大段、密密麻麻的代码,而且还没有任何的注释时常常感慨不已,深深体会到了这项工作的重要。

由于分工的需要,我们写的代码难免需要别人去阅读和维护的。

而对于许多程序员来说,他们很少去阅读和维护别人的代码。

正因为如此,他们很少关注代码的可读性,也对如何提高代码的可读性缺乏切身体会。

有时即使为代码编写了注释,也常常是注释语言晦涩难懂形同天书,令阅读者反复斟酌依然不明其意。

针对以上问题,我给大家以下建议:1)不要编写大段的代码如果你有阅读他人代码的经验,当你看到别人写的大段大段的代码,而且还不怎么带注释,你是怎样的感觉,是不是“嗡”地一声头大。

各种各样的功能纠缠在一个方法中,各种变量来回调用,相信任何人都不会认为它是高质量的代码,但却频繁地出现在我们编写的程序了。

如果现在你再回顾自己写过的代码,你会发现,稍微编写一个复杂的功能,几百行的代码就出去了。

一些比较好的办法就是分段。

将大段的代码经过整理,分为功能相对独立的一段又一段,并且在每段的前端编写一段注释。

这样的编写,比前面那些杂乱无章的大段代码确实进步了不少,但它们在功能独立性、可复用性、可维护性方面依然不尽人意。

从另一个比较专业的评价标准来说,它没有实现低耦合、高内聚。

我给大家的建议是,将这些相对独立的段落另外封装成一个又一个的函数。

许多大师在自己的经典书籍中,都鼓励我们在编写代码的过程中应当养成不断重构的习惯。

我们在编写代码的过程中常常要编写一些复杂的功能,起初是写在一个类的一个函数中。

随着功能的逐渐展开,我们开始对复杂功能进行归纳整理,整理出了一个又一个的独立功能。

这些独立功能有它与其它功能相互交流的输入输出数据。

当我们分析到此处时,我们会非常自然地要将这些功能从原函数中分离出来,形成一个又一个独立的函数,供原函数调用。

在编写这些函数时,我们应当仔细思考一下,为它们取一个释义名称,并为它们编写注释(后面还将详细讨论这个问题)。

另一个需要思考的问题是,这些函数应当放到什么地方。

这些函数可能放在原类中,也可能放到其它相应职责的类中,其遵循的原则应当是“职责驱动设计”(后面也将详细描述)。

在编写代码的过程中,通常有两种不同的方式。

一种是从下往上编写,也就是按照顺序,每分出去一个函数,都要将这个函数编写完,才回到主程序,继续往下编写。

而一些更有经验的程序员会采用另外一种从上往下的编写方式。

当他们在编写程序的时候,每个被分出去的程序,可以暂时只写一个空程序而不去具体实现功能。

当主程序完成以后,再一个个实现它的所有子程序。

采用这样的编写方式,可以使复杂程序有更好的规划,避免只见树木不见森林的弊病。

有多少代码就算大段代码,每个人有自己的理解。

我编写代码,每当达到15~20行的时候,我就开始考虑是否需要重构代码。

同理,一个类也不应当有太多的函数,当函数达到一定程度的时候就应该考虑分为多个类了;一个包也不应当有太多的类在aside的源码中,对包的组织是比较规范的,但很多action中存在大量大段代码的函数并且缺少注释,比较典型的有ModuleInfoAction、FileInfoUploadAction,QueryModelAction。

里面都有好几百行的函数,甚至一个700行的类一个函数就占了一大半。

2)释义名称与注释我们在命名变量、函数、属性、类以及包的时候,应当仔细想想,使名称更加符合相应的功能。

我们常常在说,设计一个系统时应当有一个或多个系统分析师对整个系统的包、类以及相关的函数和属性进行规划,但在通常的项目中这都非常难于做到。

对它们的命名更多的还是程序员来完成。

但是,在一个项目开始的时候,应当对项目的命名出台一个规范。

譬如,新增记录用new或add 开头,更新记录用edit或mod开头,删除用del开头,查询用find或query开头。

使用最乱的就是get,get开头的函数应该仅仅用于获取类属性。

在aside中,ModuleInfoAction这个类的命名就非常奇怪,这是一个用来做走查的类,却起了这样一个名字,看的我非常迷惑。

MissionAction,MissionManager中的命名也非常古怪,getMissionTargets02,getMissionTargets这样的函数命名很难理解注释是每个项目组都在不断强调的,可是依然有许多的代码没有任何的注释。

为什么呢?因为每个项目在开发过程中往往时间都是非常紧的。

在紧张的代码开发过程中,注释往往就渐渐地被忽略了。

利用开发工具的代码编写模板也许可以解决这个问题。

用我们常用的Eclipse为例,在菜单“window>>Preferences>>Java>>Code Style>>Code Templates>>Comments”中,可以简单的修改一下。

“Files”代表的是我们每新建一个文件(可能是类也可能是接口)时编写的注释,我通常设定为:Java代码1./*2. * created on ${date}3. */“Types”代表的是我们新建的接口或类前的注释,我通常设定为:Java代码1./**2. *3. * @author ${user}4. */第一行为一个空行,是用于你写该类的注释。

如果你采用“职责驱动设计”,这里首先应当描述的是该类的职责。

如果需要,你可以写该类一些重要的方法及其用法、该类的属性及其中文含义等。

${user}代表的是你在windows中登陆的用户名。

如果这个用户名不是你的名称,你可以直接写死为你自己的名称。

其它我通常都保持为默认值。

通过以上设定,你在创建类或接口的时候,系统将自动为你编写好注释,然后你可以在这个基础上进行修改,大大提高注释编写的效率。

同时,如果你在代码中新增了一个函数时,通过Alt+Shift+J快捷键,可以按照模板快速添加注释。

2.可维护性软件的可维护性有几层意思,首先的意思就是能够适应软件在部署和使用中的各种情况。

从这个角度上来说,它对我们的软件提出的要求就是不能将代码写死。

1)代码不能写死应该通过定义一些常量或者通过一个属性文件可以修改,也不要把所有变量都定义在一个地方,对于一些有限制的属性,定义成int或string是非常难维护的并且不容易在编译期检查到错误,java5开始提供了枚举这种数据类型,我们要善于利用。

据一个例子,baseLine.setStatus("3"); 不可读,一旦状态发生变化,需要各处去改,用IDE无法重构,维护性很差,而且容易错。

在hibernate中有@Enumerated可以直接用来映射,可读性,可维护性会好很多,而且可以避免用户输入了乱七八糟的字符。

同样的例子还有phase这个属性。

后面在Constants 中定义了一些变量提高了可读性,但是同样要求调用者对用哪个Constants的变量很清楚,并且不能约束调用者必须要用,不能提供约束。

2)预测可能发生的变化除此之外,在设计的时候,如果将一些关键参数放到配置文件中,可以为软件部署和使用带来更多的灵活性。

要做到这一点,要求我们在软件设计时,应当更多地有更多的意识,考虑到软件应用中可能发生的变化。

软件的可维护性的另一层意思就是软件的设计便于日后的变更。

这一层意思与软件的可变更性是重合的。

所有的软件设计理论的发展,都是从软件的可变更性这一要求逐渐展开的,它成为了软件设计理论的核心。

但是在这里还要提醒的一点就是过度设计带来的问题,如果我们在设计任何功能时都考虑以后会不会有变化,然后去设计一个框架去适应这些变化,那么可能把一些简单的问题给复杂化了,因为实现一个功能和实现一个框架,然后基于这个框架实现这个功能在工作量上是有天壤之别的。

这个就需要对需求要有充分的了解,并且根据经验取得平衡。

大多数情况下直接实现功能,在必要的时候进行重构是比较好的方法。

但如果没有持续重构的保证,走一步看一步会把代码写得乱七八糟。

3.可变更性前面我提到了,软件的变更性是所有软件理论的核心,那么什么是软件的可变更性呢?按照现在的软件理论,客户对软件的需求时时刻刻在发生着变化。

当软件设计好以后,为应对客户需求的变更而进行的代码修改,其所需要付出的代价,就是软件设计的可变更性。

由于软件合理地设计,修改所付出的代价越小,则软件的可变更性越好,即代码设计的质量越高。

一种非常理想的状态是,无论客户需求怎样变化,软件只需进行适当地修改就能够适应。

但这之所以称之为理想状态,因为客户需求变化是有大有小的。

如果客户需求变化非常大,即使再好的设计也无法应付,甚至重新开发。

然而,客户需求的适当变化,一个合理地设计可以使得变更代价最小化,延续我们设计的软件的生命力。

1)通过提高代码复用提高可变更性我们对于各数据项的操作在各个模块的代码中都可以看到,甚至有些还写入到了那些复杂的SQL语句中。

在这样一种情况下,如果对数据项的命名、分类等逻辑进行修改无异于需要遍历这个项目代码。

代码复用的道理十分简单,但要具体运作起来非常复杂,它除了需要很好的代码规划,还需要持续地代码重构。

对整个系统的整体分析与合理规划可以根本地保证代码复用。

系统分析师通过用例模型、领域模型、分析模型的一步一步分析,最后通过正向工程,生成系统需要设计的各种类及其各自的属性和方法。

采用这种方法,功能被合理地划分到这个类中,可以很好地保证代码复用。

采用以上方法虽然好,但技术难度较高,需要有高深的系统分析师,并不是所有项目都能普遍采用的,特别是时间比较紧张的项目。

通过开发人员在设计过程中的重构,也许更加实用。

当某个开发人员在开发一段代码时,发现该功能与前面已经开发功能相同,或者部分相同。

这时,这个开发人员可以对前面已经开发的功能进行重构,将可以通用的代码提取出来,进行相应地改造,使其具有一定的通用性,便于各个地方可以使用。

一些比较成功的项目组会指定一个专门管理通用代码的人,负责收集和整理项目组中各个成员编写的,可以通用的代码。

这个负责人同时也应当具有一定的代码编写功力,因为将专用代码提升为通用代码,或者以前使用该通用代码的某个功能,由于业务变更,而对这个通用代码的变更要求,都对这个负责人提出了很高的能力要求。

虽然后一种方式非常实用,但是它有些亡羊补牢的味道,不能从整体上对项目代码进行有效规划。

正因为两种方法各有利弊,因此在项目中应当配合使用。

2)职责驱动设计前面我提到,当我们尝试写一些复杂功能的时候,我们把功能分解成一个个相对独立的函数。

相关主题