代码整洁与代码质量

点赞:10170 浏览:44319 近期更新时间:2023-12-21 作者:网友分享原创网站原创

摘 要:软件质量不仅依赖于架构及项目管理,而且与代码质量紧密相关.而代码质量与其整洁度成正比.将需求明确到机器可以执行的程度,就是编程要做的事,这种规约就是代码.糟糕的代码可能毁掉一家公司,混乱代码的代价是驱动生产力不断趋向零,整洁不仅与效率有关,而且关乎企业的生存.总结了如何在代码中达到cleancode,即整洁和干净代码的经验规则.

关 键 词:代码整洁;代码质量;软件质量;注释;命名;类

中图分类号:TP306文献标识码:A文章编号文章编号:16727800(2013)0010003803

作者简介:曹娜(1982-),女,国际商业机器科技(深圳)有限公司上海分公司助理工程师,研究方向为软件开发、Web2.0.

1有意义的命名

终端用户响应的时间中,有80%用于下载各项内容.这部分时间包括下载页面中的图像、样式表、脚本、Flash等.通过减少页面中的元素可以减少HTTP请求的次数,这是提高网页速度的关键步骤.

减少页面组件的方法其实就是简化页面设计.那么有没有一种方法既能保持页面内容的丰富性又能达到加快响应时间的目的呢?这里有几条减少HTTP请求次数同时又可能保持页面内容丰富的技术.

1.1名副其实

要严肃对待这个问题.在起一个表意的名字上花时间是值得的,优秀程序员从细节做起.如果名称需要注释来补充,那就不是“名副其实”.

反例:

intd;//消逝的时间,以天计算

范例:

IntelapsedTimeInDays;

IntdaysSinceCreation;

IntdaysSinceModificatin;

IntfileAgeInDays;

以上问题不在于代码的简洁度,而在于代码的“模糊度”.再简短的代码,如果不能表达含义,也不能做到“名副其实”.下面反例中所示的代码够简单了,但是没人知道theList是什么东西、theList[0]的意思是什么、4是什么意义、以及返回list1该怎么用.这就是“模糊度”,因为意义比较模糊,所以这些代码也不“名副其实”.应该根据这段代码的意图来修改这里的函数名、变量名、值(4)的含义(用常量).

反例:

Ja:pulicListgetThem(){

Listlist1等于newArrayList();

For(intx:theList)

If(x[0]等于等于4)

list1.add(x);

returnlist1;

}

1.2命名要避免误导

程序员必须避免留下掩藏代码本意的错误线索.如:accountList这个名字就不太好,因为list这个词在Ja中是一个类型,如果这个名字表达的类型或者含义不是list就不应该这样命名.

1.3做有意义的区分

(1)不要用数字命名,如:a1,a2,a3.某些特定的以数字命名还是可以的,比如byteArray2String.这里2同“To”.但一般在名称后接数字是不好的习惯.

(2)废话是另一种没有意义的区分.如:有一个类叫Product类,那么ProductInfo与ProctductData就是没有意义的区分,因为它们含义几乎一样.如果约定Product是实体类,ProductInfo是纯数据,那么还是有意义的,可以接受的.但是同时还存在ProductData就明显不好了,并且这样还是增加了一些理解成本,因为从名字上无法直接判断.nameString不比name好,原因很简单:name就其含义来说其类型一定是字符串,所以String是冗余的废话.这种程度的冗余笔者认为还是可以接受的,但是不能又出现nameStr、strName、string_name等.如果出现,说明命名规则有问题或者命名规则的执行有问题.

(3)使用可以读出来的名字.

(4)使用可以搜索出来的名字.比如数值型常量,可以起一个有意义的名字便于搜索.另外,一个好的名字往往可以更加准确地表达常量在特定场景里的含义.

(5)匈牙利命名法.匈牙利命名法基本上没有必要使用,主要理由是它的长处在消减,而短处却十分明显.长处减弱:现代的编译器已经智能了,完全可以无视类型的作用.但是对于弱类型的jascript来说,还是有其必要的.理由是因为它是弱类型,而为了避免类型转换自由带来的bug,保持类型前后缀还是有其作用的,这类类型前缀除了类型语义之外还有对其用法的约束.如果是纯粹的表达类型,那么大量的前后缀毫无疑问将对阅读带来障碍.弱点:冗余.除了冗余之外,匈牙利命名法还有致命的缺点:就是变化可能使类型加入命名带来“副作用”:比如phoneString或者是stringPhone,它强调了phone是string类型的,因此也就限制了phone,而phone可能需要设计或者转换为number型的.当这种转换发生时,可能需要更改phone的命名为phoneNumber而导致重新命名的代价,或者需要与别的系统通信而导致phoneNumber与phoneString同时存在而导致理解偏差.

(6)成员前缀.不必使用m_前缀来标明成员变量.一般来说使用m_前缀来作为成员变量,主要的理由是便于在开发环境的智能提示中快速找到相应的变量,但是当需要命名前缀来提高查找速度的时候,往往说明类过大了,违背了一些需要考虑的设计原则,比如“单一职责原则”.

(7)接口和实现.接口不需要以I开头,如果一定要选择一种表示接口的编码,要选择用实现区分,而不是接口的前缀命名.如:IShapeFactory和SharpFactory之间,应选择后者作为接口名.大家都知道,按照《.设计规范》,接口是应该用I前缀命名的.但是我们要理解:首先,设计者可能虽然选择接口实现,但并不需要接口和抽象类的区分,希望客户不关注这点;其次这也存在将前缀含义加入名称的一个“副作用”,当SharpFactory需要变化成抽象类时,前缀命名就是一个巨大的包袱了,必须修改所有的名称.(8)避免思维映射.不要让读者把一个名称翻译为他们熟知的名称.这里的含义应该是避免这种思维固定翻译导致的程序误读.比如说一个循环去处理字符a、b、c、xxx.如果把变量也取名为a就不好了.

(9)避免用双关语.避免将同一单词用于不同目的.统一术语用于不同概念,基本就是“双关语”了.如:add表示增加或者连接两个值,那么集合中添加一个元素就不能用add,因为含义已经不一样了.

2函数

2.1函数要短小、再短小

函数的第一条规则是要短小,第二条规则还是要短小.经验告诉我们,函数就是要短小,20行封顶最佳.每个函数都一目了然,每个函数都做一件事,而且每个函数都依序带到下一个函数,这就是函数应该达到的短小程度.

2.2只做一件事

如何判断函数是做了一件事?就是函数所做的事情都在一个抽象层级,叫“只做一件事”.如果各个层级抽象混杂在一起,显然就做了不止一件事了.但是什么叫一个抽象层级呢?取决于函数解决特定问题、以及这个问题应有的“视角层次”和这个视角层次对应的“边界”.举个例子说明“视角层次”和“边界”:问“明源软件研发体系的整体组织架构是怎样的”?观察对象:“明源软件公司”期望看到“研发体系的整体组织架构”.答:“平台、陈立刚、产品、测试”.显然陈立刚在整体组织架构这个边界是看不到的,他是平台内部的.首先期望观察的结构是整体组织架构,因此站在这个视角层级来看,平台、产品、测试这些都是一个整体.然后分别站在平台、产品、测试再往下看.推荐使用层次的do来表达函数要做的事,这样,可以有效避免函数中不同的抽象层级混杂.不管怎样,函数只做一件事情是最重要的实践,需要好好理解.

2.3每个函数一个抽象层级

函数中混杂不同的抽象层级,往往让人迷惑,读者无法判断哪些是基础概念哪些是细节.读者无法提纲挈领的代价是:它无法快速学习、快速理解,从而为更多的bug埋下隐患.

2.4使用描述性名称

如:testtableHtml?SetupTeardownIncluder.render.

如果长一点的名称可以更加清晰,不要犹豫,用清晰的吧.

2.5函数参数

最好是0,其次是1个,再次是2个,避免3个及以上的参数个数.参数过多会使得客户程序员上手的代价加大,优秀代码的可能性降低.

参数与函数名位于不同的抽象层级,它要求必须了解目前并不特别重要的细节.

输出参数比输入参数更加难以理解.一般只有在特定的情况下才使用输出参数.比如C#中Int.TryParse(),为了避免使用无必要的异常表达错误,提供了输出参数.


这里的标志参数指的是通过特定的参数来控制函数的行为,比如Log(xxxxx,true).这里true/false控制函数是否记录日志.标识参数明显违背了函数只做一件事的原则.

另外,标识参数有一些变体,比如传输一个整型值,根据具体值来做判断到底该做何事.

二元参数比一元参数更难理解,如assertEquals(excepted,acutal),经常会把参数的位置搞反.有2个参数读者和客户就要考虑顺序问题,如果命名又不佳的话,就会经常弄错.

三元参数比二元更加难懂.如果参数看起来需要两个、3个或者3个以上参数就说明一些参数应该封装为类.至少达到3个就要认真考虑是否应该合并参数,是否过于陷入细节了.

2.6抽离Try/Catch代码

将try/catch代码隔离出来,避免影响主程序逻辑,例:

Try{

DeletePage(page);//DeletePage是一个方法

}

Catch(Exceptione){

LogError(e);//LogError是一个方法

}

错误处理就是一件事.try/catch总是单独出现的,里面最好不要包含普通语句,如上例.

代码整洁与代码质量参考属性评定
有关论文范文主题研究: 关于注释的论文范文检索 大学生适用: 硕士毕业论文、函授毕业论文
相关参考文献下载数量: 19 写作解决问题: 本科论文怎么写
毕业论文开题报告: 论文提纲、论文结论 职称论文适用: 论文发表、职称评中级
所属大学生专业类别: 本科论文怎么写 论文题目推荐度: 最新题目

3注释

3.1核心观念

注释的恰当用法是弥补在用代码表达意图时遭遇的失败.注释会撒谎,这个比较令人费解,但是它真实地存在于我们的系统中,并且是大量存在.原因很简单,程序员不能坚持维持注释,程序存在的时间越久,注释的可信度、可读程序就很低.程序员虽然有保持注释精读的责任,但是更应该做的是整理代码,减少注释,注释过多往往是一种程序“腐化的坏味道”.

3.2好的注释

不可省略地涉及到法律的信息,比如开源协议.提供信息的注释应该是用好名称不易传达的补救措施.

例:

//returnsaninstanceoftheresponderbeingtested.

ProtectedabstractResponderresponderInstance();

不如:

ProtectedabstractResponderresponderBeingTested;

如果找不到responderBeingTested这样的好名字,则用注释提供信息是好的做法.但是应该首先尽力去想一个可以表达意图的有意义的名字.

对意图进行解释.对某些可以选择的实现决定进行解释.

将一些晦涩难懂的参数或者返回值的意义编译为更加可读的形式.

警示.这里指的是一些特定行为的代码注释.比如某个测试可能会运行很长时间之类的注释.

TODO注释.未完成的列表.完成后要删除掉.

3.3坏的注释(1)喃喃自语.这种情况大量存在,属于程序员的自言自语,基本是垃圾代码的借口或者错误决策的修正.

(2)多余的注释.大量存在没有什么意义的废话注释.

(3)误导性注释.不够精确或者干脆写错了.

(4)循规式注释.指的是应文档化工具的需求就添加的本来不需要注释的注释.

(5)日志式注释.指的是本代码文件的修改历史类,将每天的修改记录写上,这完全没有必要,可以被现代源码管理工具取代,它影响了代码阅读.

(6)废话注释.

(7)可怕的废话注释.

(8)能用函数或者变量的时候就不用注释.

(9)位域标记.这类注释用于标记一个特别的位置.这种用法应该在特定的情况下使用,但是多数属于不必要的滥用.

(10)括号后的注释.这里指的是代码太长了,在}后添加注释表示×××代码段结束.这类注释一般是程序需要整理的标志.

(11)归属于署名.现在源码管理可以取代,基本不需要了.

(12)注释掉的代码.

(13)HTML注释.

(14)非本地信息.

(15)信息过多.

(16)不明显的联系.

4格式

对格式的研究也是非常严谨的.以多个著名优秀开源项目的实际代码格式分析来研究,诸如一行应该多少个字符,应该空几个这样的看似细小的问题.其中对“顺序”、“缩进”的处理、这些处理对读者理解之间的影响研究等.通过对类的成员:共有、私有成员变量、方法进程“顺序排列”,通过“缩进”或者“不缩进”,表达语义远近的研究,还是非常有借鉴意义的.

5类

对于函数,通过计算代码行数衡量大小.对于类,我们采用不同的衡量方式计算权责.权责同职责:

①类应该符合单一权责原则;②类应该内聚;③保持内聚会得到需要短小的类.

关于类的组织这个话题比较大,比较原则性,要注意理解不同设计原则之间的因果依赖关系.