一段优秀的代码,它一般需要满足以下几个条件:

#统一规范#

所有的代码,第一前提必须是统一规范,而常见的统一规范主要包括有以下内容:

1)统一编辑器规范

在团队开发中,我们并不对各个开发人员使用的编辑器做硬性要求,你可以使用常见的如Eclipse、WebStrom、Sublime等集成开发环境IDE,也可以使用UltraEdit这种,甚至Vim都无所谓。但是我们还是会做一些统一规范要求,比如Tab键统一使用空格Space替换(一个Tab为四个Space)、文件编码统一为UTF-8、换行的Line Delimiter统一为Unix的LF而不是Windows的CRLF(PHP代码尤其重视这点)等等。

2)统一代码风格

我们知道代码的风格有很多种,尤其是在涉及到括号的使用上,比如:

  • K&R括号风格

  K&R风格是最早为人们所喜爱的放个,它是由C语言之父Kernighan和Ritchie在他们的《C程序设计语言》一书中确立的,它也常被认为是最初和最好的风格,它可以在一个小屏幕中尽可能显示更多的信息。这个也是我个人最常用的风格。

  1. int k_and_r() {
  2. int a = 0, b = 0;
  3. while (a != 10) {
  4. a++;
  5. b++;
  6. }
  7. return b;
  8. }
  • 悬挂式括号风格

  悬挂式的风格在空间上显示上更加开阔,由于有着更明显的前括号,也使得代码更加易于浏览;但在竖向空间上占用更多。

  1. int exdented()
  2. {
  3. int a = 0, b = 0;
  4. while (a != 10)
  5. {
  6. a++;
  7. b++;
  8. }
  9. return b;
  10. }
  • 缩进的括号风格

  缩进风格并不太常见,在这种风格下括号随代码一起缩进,这种风格也被称为“Whitesmith”风格,因为早期的Whitesmith的C编译器的示例代码使用的就是这种风格,个人并不推荐。

  1. int indented()
  2. {
  3. int a = 0, b = 0;
  4. while (a != 10)
  5. {
  6. a++;
  7. b++;
  8. }
  9. return b;
  10. }
  • 其他风格

  还有一些其他的括号风格,比如GNU风格是介于悬挂式和缩进式风格之间的一种风格,括号被放置在各个缩进级别的一半的位置

3)统一命名规范

命名包括文件的命名、类的命名、方法的命名、变量的命名等,良好的命名使得代码易于阅读,也更加易于维护。命名的方法有很多,常见的的有:

  • 匈牙利命名法

  匈牙利命名法是一种有争议的命名约束,它将关于变量或函数的类型的信息编入它们的名称当中,要求开头字母使用变量类型的缩写,其余部分用变量的英文或中文的缩写,同时要求单词的第一个字母大写。这种命名法最初是在20世纪80年代的Microsoft公司中出现的,并在该公司的Win32 API和MFC库中得到了广泛的使用,也因此导致了一定的流行性。之所以被称为“匈牙利命名法”,是因为它的创始人Charles Simonyi是匈牙利人。此外,变量名看起来像是使用匈牙利语书写的,但是要理解它并不容易,很多非Windows的程序员都会被比如lpszFile、rdParam和hwndItem等的奇怪名字给搞糊涂。

  1. int iMyAge; // "i"是int类型的缩写
  2. char cMyName[0]; // "c"是char类型的缩写
  • 驼峰式命名法

  有的时候又称为“小驼峰命名法”,它在Java语言库以及很多C++代码库中得到了广泛使用,这种命名主要源于其大写字母的布局很像骆驼的驼峰,它规定第一个单词字母小写,后面其他的单词首字母大写。

  1. int myAge;
  2. char myName[0];
  • 帕斯卡命名法

  这种命名法跟上面的驼峰命名法很相像,唯一区别就是其第一个字母也大写,所以有时又称为“大驼峰式命名法”。

  1. int MyAge;
  2. char MyName[0];
  • 下划线命名法

  这种风格在C++标准库和GNU Foundation中比较常见,也即用下划线来隔开不同的单词。

  1. int my_age;
  2. int my_name[0];

事实上,采用什么样的命名方式都是可以的(虽然我个人更偏向于驼峰命名法),更重要的是命名必须清晰,比如函数名可以采用动名词+静名词的组合来命名,而不是用foo和bar这种古怪的名称。另外,建议在清晰的基础上保持简洁,否则也很可能出现类似someTypeWithMeaningfulNaming这种非常冗余的命名,比如在for循环中使用i而不是使用index就是一种推荐做法。

#简洁清晰#

什么叫做简洁?能够一句代码解决的事情,就不要写成两句代码。

什么叫做清晰?虽然一句代码能够解决事情,但我们有的时候却将其拆成了多句代码,使得其变得可能有点冗余。

优先程度上,清晰>简洁。

每个人写的代码,它的后续维护者可能是一个初级程序员,如果他不能理解你的代码逻辑,或者说你的代码逻辑很难理解,那么他就有可能会犯一些错误。复杂的结构和不常用的语言技巧虽然可以证明你在运算符优先级方面有着熟练经验,但是这些实际上会扼杀代码的可维护性。比如下面两段代码:

  1. int a = b = c = 10;
  2. int result = a * b + b * c - a + b / c;
  3.  
  4. // 下面的代码虽然不如上面代码简洁,但是更加的简单
  5. int a = 10, b = 10, c = 10;
  6. int result = (a * b) + (b * c) - a + (b / c);

清晰的代码,也往往意味着简单的代码。如果对于某段代码不是基于性能上的需要而写得复杂,那么,请保持代码的简单清晰。

#必要注释#

作为一个负责任的程序员,我们有义务给我们的代码写注释,即使在编码任务再繁重的情况下。

但我们也需要注意的是,注释不是越多越好,我们更加重视注释的质量,而不是数量。很多时候,一个好的命名已经可以能够帮我们省下很多注释。比如下面的代码:

  1. for (int i = 0; i < wlst.size(); i++)
  2. k(wlst[i]);
  3.  
  4. // 上面代码改成下面代码
  5. for (int i = 0; i < widgets.size(); i++) {
  6. printWidget(widgets[i]);
  7. }

注释中应该包含的内容包括有:

  • 解释为什么,而不是怎么样

  注释不应该描述代码是怎样运行的,这完全可以通过阅读代码来了解,你更加应该注意的是描述为什么有些东西要这么写,比如下面的代码的注释就不是必须的,完全可以去掉:

  1. // 循环遍历所有的widget
  2. for (int i = 0; i < widgets.size(); i++) {
  3. // 打印这些widget
  4. printWidget(widgets[i]);
  5. }
  • 不要描述代码

  不要试着去描述代码,比如下面的代码的注释完全是无效的:

  1. // index自增
  2. ++index;
  • 不要取代代码

  不要试图在注释中去说明某个代码的限制条件,更应该地使用代码本身的机制去实现。还有,当有的时候你发现可能要花大量的注释来描述某段代码的功能的时候,更好的做法是用代码去描述,比如把一大段的代码拆分成多个子函数,给每个子函数赋予更合适的命名等。比如:

  1. // 下面这个方法不允许被类以外访问
  2. public void getMyAge() {
  3. }
  4.  
  5. // 完全可以不要上面的注释,而使用如下的代码实现
  6. private void getMyAge() {
  7. }
  • 避免给代码造成分心

  注释不应该给本身的代码造成分心,比如有些程序员喜欢在if的结束加上// end if (a < 1) 这样的注释,而这种注释则是完全没必要的,只会给原本阅读代码造成分心,更合适的做法是通过正确合适的缩进方式等来保证代码可读性。

#健壮安全#

我们在编写代码的时候,需要考虑各种方方面面的因素,提高代码的健壮性。比如,在编写最常见的登录代码时,就需要考虑到用户输入的用户名和密码的多种情况,比如:

  1. public void checkLogin(String username, String password) {
  2. if (username == null || password == null) {
  3. // Error
  4. }
  5. if (username.trim().equals('') || password.trim().equals('')) {
  6. // Error
  7. }
  8. // do something
  9. }

不能只是简单地检查用户名和密码是否匹配,还必须要考虑到当用户名或密码为空时候的处理逻辑,如果用户名是手机号,还必须使用正则表达式来检查所输入的用户名是否符合手机号码样式等等。

不仅于此,对于安全性上的要求,还必须要对用户输入的字符进行处理,防止用户输入类似<script>alert()</script>的XSS攻击代码和类似'or'1'='1的SQL注入攻击代码等。

总之,要编写健壮安全的代码,必须时刻考虑到:

  • 使用“防御性编程”

  “防御性编程”中心原则是“不做设想”,也即永远不要设想用户会按照我们写代码的预期或要求来使用代码。一些简单的防御性规则,比如“检查所有的输入”和“验证所有的运算”,可以帮我们把代码中许多的安全隐患给消除掉。

  • 代码审核

  初级开发人员的代码在早期必须经由高级开发人员进行代码审核,有经验的高级开发人员能够通过阅读代码就能发现很多问题,比如边界值的判断上,数组是否会造成溢出和字段长度是否超出数据库限制等这些都是可以通过代码审核来发现。

  • 严格地执行测试和调试,尽可能地消除Bug。

  一些敏捷开发的TDD测试驱动开发就是这么做的,它要求代码必须是可以被测试的,并且这些可以被测试的代码时刻能够通过测试样例。

#高效性能#

随着现在处理器的越来越强大,很多时候应用的瓶颈并不在代码而在其他地方(主要是I/O操作,比如读写文件和数据库等),个人更不推荐为了所谓的性能而牺牲了代码的可读性和可维护性,除非这块代码确实是必须优化的。

但我们也不能仗着处理器的强大就可以完全不顾代码的性能,有些简单的优化原则在我们写代码的时候就需要时刻遵循。

  • 将工作推迟到必须时再做

  如果不是马上就要使用某个文件,那么就不要打开它;如果暂时不需要某个值,那就不要去计算它;如果没有某个函数程序也可以运行,那就不要去调用它。

  • 在函数中做进一步检查以避免多余的工作

  如果一个可能会导致函数计算无效的条件,那么对这个条件的判断最好放在顶部,防止做了过多无用工作,比如:

  1. public int calculate(int number) {
  2. if (number == 0) {
  3. return 0;
  4. }
  5. // do something
  6. }
  • 将不变条件的计算移到循环外

  由于循环中是每次都要做的,那么每次循环中如果某个值都保持不变,则将该值移到循环外面,比如:

  1. for (int i = 0; i < tree.appleCount(); i++) {
  2. }
  3.  
  4. // 上面代码可以改成如下
  5. int appleCount = tree.appleCount();
  6. for (int i = 0; i < appleCount; i++) {
  7. // do something
  8. }
  • 利用“短路求值法”

  确保将可能失败的测试放在最前面以节省时间,比如if (condition_one && condition_two),确保condition_one不为真的可能性比condition_two更大。

  • 不要重复进行相同的工作

  比如将公共的代码提取到共享函数中以避免重复计算,或者将某个经常被用到的需要被计算出来的值放到缓存中以备使用。

最后,各种不同的编程语言都有其不同的优化之处,比如Java中使用增强式foreach循环就比传统的for循环更加高效,具体到各种语言再具体分析。

#易于扩展#

首先,我们对于程序的扩展性判断,并不是需要其能支持未来需求的变更的可能性。相反,我们不提倡对代码做“过度设计”,代码的可扩展性更主要的是体现在未来能够对需求变化快速响应上,能够跟随需求的变化而快速变化,而不是一味不变地支持需求的变化。

但是这也肯定也不能成为我们偷懒的借口,我们还是可以使用一些常规的原则来增强代码的可扩展性,尤其是时刻保证代码的“低耦合”非常重要。

  • 动态按需加载

  不同的功能拆分出来,而不是在一个函数里完成,比如:

  1. public void init() {
  2. // load database
  3. // init environment
  4. // init ui
  5. // do something
  6. }
  7.  
  8. // 上面的方法把所有的事情都放在一起,扩展性很差,我们需要把不同的功能拆分成不同的函数
  9. public void loadDatabase() {}
  10. public void initEnvironment() {}
  11. public void initUi() {}
  12. public void init() {
  13. loadDatabase();
  14. initEnvironment();
  15. initUi();
  16. }

对于配置文件也是一样,我们需要在程序的入口处能够根据当前环境不同而加载不同的配置文件,比如Web应用中常见的做法是将本地环境、测试环境、正式环境分成三个配置文件,然后根据当前请求域名的不同而加载不同的文件,而不是把所有的配置写到一个文件里。

  • 抽象接口

  对于一些功能类似的,完全可以抽象出一个抽象类,在这个抽象类中定义功能接口,然后再在不同的子类中实现该接口的不同功能,比如Web应用中一种常见的做法是在抽象控制类中定义GET/POST/PUT/DELETE请求协议的接口方法,然后再在不同子类中分别实现不同的功能细节。

  1. public abstract class BaseController {
  2. public void get();
  3. public void post();
  4. public void put();
  5. public void delete();
  6. }

#写在最后#

上面写了这么多,其实说白了提高写代码能力唯一的途径就是多写代码,多思考。对于初级程序员来说,把代码写得具有可维护性(规范/简洁/注释)是最基本的要求,而对于有经验的程序员来说,还必须考虑代码的健壮性、安全性、高效性和可扩展性等。写代码的时候多考虑下这些东西,经常重构自己的代码,就离写出优秀的代码不远了。

最后,给大家推荐几本个人觉得不错的书籍,这几本书对我的代码生涯有着极大的影响。

1、《编程匠艺 编写卓越的代码》,电子工业出版社,Pete Goodliffe(著),韩江, 陈玉(译);

2、《重构 改善既有代码的设计》,人民邮电出版社,Martin Fowler(著),熊节(译);

3、《大话设计模式》,清华大学出版社,程杰(著);

4、《Java优化编程(第2版)》,电子工业出版社,林胜利, 王坤茹(编著);

优秀代码要求(转自http://www.cnblogs.com/brishenzhou/p/6284188.html)的更多相关文章

  1. 优秀代码摘录片段一:LinkedList中定位index时使用折半思想

    在LinkedList有一段小代码,实现的功能是,在链表中间进行插如,所以在插如的过程中会需要找到对应的index位置的node元素: 如果放在平时只为了实现功能而进行遍历查找,很多人会直接使用一个w ...

  2. 页面定制CSS代码初探(四):cnblogs使用Github引用样式

    前言 对于用惯了Github的人来说,眼里的引用应该是这样的 "Talk is cheap. Show me the code" -- Linus Torvalds 然而实际上cn ...

  3. patchwork.ffmpeg.org 里面未被选中的优秀代码

    很多程序员为 FFMpeg 增加新功能写出代码, 把写好的代码 git send-email 邮件方式提交 patch 文件 发送给 patchwork.ffmpeg.org; 一直认为 FFMpeg ...

  4. 一道仅有7人通过的超5星微软比赛题目-------解题思路&优秀代码分享,邀你来“找茬儿”

    6月23日英雄会平台发布了一道难度为超5星的微软比赛题目,截止活动结束共有300多名编程爱好者参与线上答题,而最终通过者仅有7人,通过率仅为2%.为什么成绩如此出人意料?是因为题目的英文描述难以理解? ...

  5. webstrom 代码工具(转http://www.cnblogs.com/tangdanni11/p/5149063.html)

    Emmet的前身是大名鼎鼎的Zen coding,如果你从事Web前端开发的话,对该插件一定不会陌生.它使用仿CSS选择器的语法来生成代码,大大提高了HTML/CSS代码编写的速度,比如下面的演示: ...

  6. 30秒就能理解的JavaScript优秀代码

    数组 arrayMax 返回数组中的最大值. 将Math.max()与扩展运算符 (...) 结合使用以获取数组中的最大值. const arrayMax = arr => Math.max(. ...

  7. PNotes – 目前最优秀的桌面便签软件 - imsoft.cnblogs

    Pnotes: 下载链接: http://pan.baidu.com/s/1o6FK4SM 密码: n7il 便携版,包含中文语音包,包含十几种合适的皮肤. 更多信息:小众软件 http://www. ...

  8. 【转】如何在github上fork一个项目来贡献代码以及同步原作者的修改 -- 不错

    原文网址:http://www.cnblogs.com/astwish/articles/3548844.html 作为一个IT人,通过github进行学习是最快的成长手段.我们可以浏览别人的优秀代码 ...

  9. Programming好文解读系列(—)——代码整洁之道

    注:初入职场,作为一个程序员,要融入项目组的编程风格,渐渐地觉得系统地研究下如何写出整洁而高效的代码还是很有必要的.与在学校时写代码的情况不同,实现某个功能是不难的,需要下功夫的地方在于如何做一些防御 ...

随机推荐

  1. NHibernate总结

    NHibernate总结 现在的项目中数据访问使用的是NHibernate的一个ORM框架,小弟也是在后期加入项目组,之前对NHibernate就一直没有接触过,所以一直在学习NHibernate,都 ...

  2. MFC注册表操作

    注册表简介 有时程序中要存些设置信息,一个方法就是创建一些普通的txt或xml文件,然后保存进去就行了.另一办法就是保存到注册表里.注册表是由windows维护的一个小数据库.里面也会保存window ...

  3. 最近修bug的一点感悟

    写在前面话 项目从13年1月份,现场开发,4月中旬,项目开发接近尾声,三个开发,留两个在现场,我被调回公司,5月份现场一同事离职,只有一个同事在开发,结果PM想让这一个同事承担余下的开发和bug工作, ...

  4. PHP curl之爬虫初步

    php的curl可以实现模拟http的各种请求,这也是php做网络爬虫的基础,也多用于接口api的调用. 这个时候有人就要发问了:为什么你特么不用file_get_contents? curl的性能比 ...

  5. 微信小程序的动画效果

    前言 由于公司计划有变,所以从H5页面改成去小程序写.所以在着手开发小程序.本人也不是什么前端高手,只是一名写后端偶尔写写前端的渣渣.请前端大神们勿喷. 一.什么是微信小程序? 小程序在我的理解中只是 ...

  6. .NET基础——运算符

    这一篇我们来讲解C#中的运算符 1. C#中的算术运算符 5个算数运算符:+  -  *  /  %     它们都是二元运算符,*  /  % 的运算优先级相同,并且高于 +  - ,+  - 的运 ...

  7. Windows卸载软件出现蓝屏SYSTEM SERVICE EXCEPTION(VrvProtect_x64_2.sys)

    今天给大家介绍一个卸载Windows上软件的工具Windows Installer Clean Up,可以卸载电脑上的很多控制面板里面卸载不掉的软件,或者卸载过程中出现问题的软件. (1)出现的现象: ...

  8. ADO.NET初学习

    ①System.Data  → DataTable,DataSet,DataRow,DataColumn,DataRelation,Constraint,DataColumnMapping,DataT ...

  9. 遇到delphi连接sql一个奇怪的问题:未指定的错误,加大了命令的等待时间为600即可了

    遇到delphi连接sql一个奇怪的问题:未指定的错误,加大了命令的等待时间为600即可了 找了一下午没解决.

  10. MySql开启远程访问(Linux)

    Linux服务器上安装了MySql数据库服务器之后,在远程访问出现了61错误.经检查后,发现需要在MySql配置文件中取消绑定IP.具体做法如下: 打开my.cnf配置文件.连接到服务器之后,在终端中 ...