我现在是这样编程的

标签: 编程 | 发表时间:2013-08-13 15:56 | 作者:
出处:http://kb.cnblogs.com/

   我在做什么

  曾经,我试过接到一些需求。一眼带过后,脑袋马上随着高昂的斗志沉溺在代码的世界中 ,马不停蹄地敲着键盘直到最后测试的完成。我从思绪中恢复过来,乍一看自己写的功能,和需求差了十万八千里,我TM都在干嘛?

  除此之外,我还见过类似的很好笑的事情。有一个程序员,经理提了需求,然后他在那里折腾了一天。结果不但没做出来,而且和实际需求都是完全搭不上调。经过询问发现,他不知道经理说了什么,也不知道自己到底在做什么。

  代码的世界可能是昏天暗地的,但是我们的思维不能这样随之混乱,否则一切都会前功尽弃。所以我现在编写程序的时候,经常会想一下:我要做什么,我在做什么。更好的方法是把详细需求落实到文档,并时刻核对文档。

  大局为重

  2-8法则告诉我们,一个项目核心的功能只有很少,其它大部分都是对核心功能辅助或增强的。但当任务分发下来,我手头总有一些自己很想开发的模块,不过它们不属于那20%。我以前经常会在这些感兴趣的模块上花费很多时间和精力。

  结果项目快要到上线期限,主要的功能却没开发完成,其它一些不起眼的功能却做得很好,但为此项目不得不延期了。如果反过来,只要对整体功能预期不会有太大偏差,可以将就的先上线。重要一点是:即使功能还有遗漏,但项目可以上线了,老板自然不会太追究,自己工作也能图个安心。如果不知道那些功能模块是最重要的,先问问经理。

  人总是喜欢做一些自己感兴趣或者有挑战的事。不过在这方面,为了项目和团队着想,应该尽量压制这种诱惑。

  性能永远不是优先考虑的问题

  我从来不会一开始就考虑性能问题。如果项目成本很低,甚至到项目结束时,如果没有感觉到明显的性能问题,也不会去管。要知道现在已经不是DOS的年代,CPU的计算能力很高,但成本很低了。重要一点是,如果只针对提升性能对代码做改动,很容易破坏代码的复用性和可维护性。而返过来,提高了代码的复用性和可维护性,则很容易提高性能。

  下面有一个 PHP的代码实例,功能是帮助用户重置密码(代码为了简单说明问题,请不要太在意一些无关的细节)

  requestResetPassword是接收用户重置密码的请求并且做了相应的检查。为了更好的复用性,我将重置密码的操作单独分配到一个新的 resetPassword的函数,更改完密码的后再调用 sendEmail向用户发送一封通知邮件。

01 /**
02 * 用户请求重置密码的接收器
03 */
04 function requestResetPassword() {
05 //检查用户是否存在
06 if( !checkUserExists( $_GET['userid'] ) ) {
07 exit('抱歉,用户不存在,请确认用户帐号。');
08 }
09 resetPassword( $_GET['userid'] );
10 //最后向用户发送一封邮件
11 sendEmail( $_GET['userid'], '重置密码成功', '新的密码是xxxx' );
12 exit('新密码已经发送到你的邮箱。');
13 }
14
15
16 /**
17 * 帮助用户重置密码
18 */
19 function resetPassword( $userid ) {
20 //检查用户是否存在
21 if( !checkUserExists( $userid ) ) {
22 return false;
23 }
24
25 //进行重置用户密码的操作
26 //略...
27 return true;
28 }
29
30
31 /**
32 * 向用户发送一封邮件
33 */
34 function sendEmail( $userid, $title, $content ) {
35 //检查用户是否存在
36 if( !checkUserExists( $userid ) ) {
37 return false;
38 }
39
40 //发送邮件操作
41 //略...
42 return true;
43 }
44
45
46 /**
47 * 检查某个用户是否存在
48 */
49 function checkUserExists( $userid ) {
50 $user = getUserInfo( $userid );
51 return !empty( $user );
52 }
53
54
55 /**
56 * 获取某个用户的数据
57 */
58 function getUserInfo( $userid ) {
59 //假设我有一个query的函数,它用来查询数据库并返回数据
60 $user = query( "SELECT * FROM `user` WHERE `uid`=" . intval( $userid ) );
61 return is_array( $user ) ? $user : array() ;
62 }

  现在问题是,这三个函数都同时使用 checkUserExists这个函数来检查用户不存在,数据库查询了三次,这样带来了一些额外的开销。

  如果要去掉三者之间任意一个 checkUserExists,看上去是可能的。但是如果之后有某些功能要调用 resetPassword或者 sendEmail,用户不存在时,系统可能会发生错误。

  还有一个解决方法是,将 resetPassword的逻辑写到 requestResetPassword里,再过一点,把 sendEmail的逻辑也写进去。这样函数调用减少,数据库查询也变成一次了,性能得到了提高。但是重置密码和发送邮件的功能将不能得到复用,并且违背了 单一责任的原则,代码复杂度也提高了。

  不过,因为函数分离和复用性都很好,如果实际性能受到影响,可能考虑用缓存的方法减少数据库查询,我改动了它们共用的 checkUserExists函数:

01 /**
02 * 检查某个用户是否存在
03 */
04 function checkUserExists( $userid ) {
05 //增加一个缓存,用以记录检查用户的结果
06 static $cache = array();
07
08 //检查当前用户是否已经检查过一次
09 if( isset( $cache[ $userid ] ) ) {
10 return $cache[ $userid ];
11 }
12
13 $user = getUserInfo( $userid );
14 //把结果记录到缓存中
15 $cache[ $userid ] = !empty( $user );
16
17 return $cache[ $userid ];
18 }

  也可以用同样的方法改动 getUserInfo函数。

  这里可以看到,当代码的复用性提高时,想提高性能是很简单的,性能的瓶颈也很容易被发现和修改。

  尽管这个例子对性能影响还不够大,还有一些影响更大的,比如说遍历,我可能为了复用而将遍历封装到一个函数中,并且多次使用它。这些开销对我的项目根本没有预想中那样有太大的影响,或者说是微乎其微的。所以我更愿意把时间花在如何提高代码的复用性和维护性方面,而不是纠结于浪费多这一点性能。实际性能如果真的达不到要求,也可以权衡增加硬件配置。

   名字长一点好

  函数名和变量名等除了给机器看,也要给人看的,有时一个简单直接的好名字实在是很难想,这时不妨用长一点的名字更好。可读性更好:

01 //好名字
02 class ErasedTypeEquivalence {
03 }
04
05 //坏名字
06 class ErdTypeEqe {
07 }
08
09 //好名字
10 function checkUserExists () {
11 }
12
13 //坏名字
14 function ckUserExt() {
15 }
16
17 //好名字
18 $result;
19
20 //坏名字
21 $ret;

  我见过一些代码,由于简单写过多,整遍代码很多都是4个字母或以下的,可读性非常差,当然不排除是为了偷懒。

  但如果想有更多的时间腾出来偷懒,不应该在这上面玩小聪明,否则这时我现在应该在思考前几天的代码是在写什么。

  什么?短名字会让代码执行得更快? 那证明给我看,如果真的快,快了多少?

   自说明代码很重要,但注释同样重要

  代码本身可以说明问题的确是很棒的,但并不是说注释不重要,有时候我更喜欢先看注释,因为它总比我看代码更快的了解这程序是做什么的。

  如果我把本文前面说性能的例子去掉注释,哪个能让你更快了解代码的意图?或者说,你更愿意看哪个?

01 function requestResetPassword() {
02 if( !checkUserExists( $_GET['userid'] ) ) {
03 exit('抱歉,用户不存在,请确认用户帐号。');
04 }
05 resetPassword( $_GET['userid'] );
06 sendEmail( $_GET['userid'], '重置密码成功', '新的密码是xxxx' );
07 exit('新密码已经发送到你的邮箱。');
08 }
09
10
11 function resetPassword( $userid ) {
12 if( !checkUserExists( $userid ) ) {
13 return false;
14 }
15
16 //进行重置用户密码的操作
17 //略...
18 return true;
19 }
20
21
22 function sendEmail( $userid, $title, $content ) {
23 if( !checkUserExists( $userid ) ) {
24 return false;
25 }
26
27 //发送邮件操作
28 //略...
29 return true;
30 }
31
32
33 function checkUserExists( $userid ) {
34 static $cache = array();
35
36 if( isset( $cache[ $userid ] ) ) {
37 return $cache[ $userid ];
38 }
39
40 $user = getUserInfo( $userid );
41 $cache[ $userid ] = !empty( $user );
42
43 return $cache[ $userid ];
44 }
45
46
47 function getUserInfo( $userid ) {
48 $user = query( "SELECT * FROM `user` WHERE `uid`=" . intval( $userid ) );
49 return is_array( $user ) ? $user : array() ;
50 }

  所以,即使代码本身很清晰,但是加上注释的话,可读性也能提高很多!

   适当抽象

  编程就是为了解决实际中的问题,在思考如何编码的时候,把问题抽象到一定的高度去思考,更容易把握问题所在。不过更多时候,我发现从代码抽象到现实的例子是有一定难度的,同时我也相信,编程高手也是抽象高手,他们很容易把问题反映到真实生活中去。

  不过如果经常留意和思考生活中的细节,会提升自己的抽象能力。

  举一个螺丝刀的例子,如果叫你造一个螺丝刀,你会做成什么样子?我这里有三把不同的螺丝刀:

  显然第一种螺丝刀是最简单的,比较中规中矩。

  第二种螺丝刀中间可以旋转刀柄,让刀柄和刀头成90度,这样的设计让拧螺丝更加轻松。

  第三种螺丝刀则可以更换刀头,如果以后有其它类型的螺丝,则只要造一个适合这种螺丝的刀头就可以了。

  那反映到编程中的问题,如果项目要增加一个工具类库。

  第一种方法,可以直接把类库的所需功能写出来就可以了。

  第二种方法,不但把类库写出来,而且针对项目的一些情况做特殊改进,使得在这个项目中更好用。

  第三种方法,根据类库的特性,把公共部分的逻辑做成接口,特殊的部分分离出来单独实现,如果以后要增加相同类型的类库,则实现特殊部分的逻辑,然后接入接口即可。

  但是在抽象的时候,要避免不合理的抽象,有时也可能造成过渡设计,现在只需要一种螺丝刀,但你却把更多类型的螺丝刀都做出来了(而且还是瑞士军刀的样子。。):

   一致性

  团队开发中,可能每个人的编程风格都不一样,拿花括号来说,有些人喜欢和代码在同一行,而有些喜欢独自一行

1 //例一
2 function func() {
3 }
4
5 //例二
6 function func()
7 {
8 }

  命名风格也都不一样,比如说声明变量接收一个函数返回的数据,有些喜欢用result,有些喜欢用data。

  它们可能都很好,不过在团队开发中,尽量统一用同一种风格能够很好的减少交叉开发的成本。

   将错就错

  面对项目一些无关紧要的分歧或错误,应该要接受和理解。承接上面的问题,如果团队中已经有人大量用了data的变量命名,但你认为result的更符合当前状况的描述。这种情况,我优先选择data命名,因为如果再使用result的话,会破坏项目的一致性,对开发没有任何好处。

  这只是很少的一方面,如果项目规范没有很好的落实,实际工作中会有大量的一致性问题,必须靠团队每个人的决心和责任心去把它做好。通常,加入一个正在开发中的项目,编写功能前,我都会首先看项目之前的类似的代码,并尽量模仿他们的写法。不过,如果有明显的错误,应该及时指出和修正。

  只要坚持把一致性做好,很多方法会成为团队甚至业界的标准,即使它们不是最好的,但是有什么关系呢?

   质量至上

  现在还有人认为把时间花在提高代码质量上会降低开发进度。如果你有开发一个长期项目,最后花在修BUG的时间占整个项目的比例是多少?一个质量把握不好的项目,修BUG的时间可能占项目50%甚至更多。如果有兴趣,或者可以看一看这篇文章《 不要再问效率还是质量的伪命题》的观点,如果还不够说服力,《人月神话》《代码大全》这两本关于项目管理的书,里面有详细说明这个问题。

   适当休息

  编程的时候如果没有思路或者感到混乱,到外边休息10分钟,或者看一下风景,让脑袋清醒一下是很好的。这招很管用,亲测。

   至少把代码完整运行一次

  有时函数的逻辑过于简单,以至于会认为这个不可能发生错误,但事实上最容易发生错误的通常就是这些代码,常见的单词拼写错误,参数错误,还有一些意料之外的问题。所以无论什么情况,我都会把代码完整运行一遍。

  当然更好的做法是用一些系统的测试方法,比如说单元测试。

   编程不是艺术

  从一开始,编程语言的出现和发展,都是为了解决现实生活中的问题,包括它自身产生的问题。

  面向对象、设计模式的出现,是用来解决编程语言自身带来的可读性和维护性等问题,而不是为了让编程语言上升到艺术的层面。尽管编程中有‘优雅’一词,但我更认为它只是用来形容代码更容易让人读懂和维护。

  我拒绝一切看起来很‘优雅’,却不能为编程工作带来一点好处的代码。如果你喜欢玩弄语言,应该去当作家。

   甘于平凡

  程序员真的很高傲,在我接触过的人中,包括我自己也是。我以前经常对一些简单的代码感到不屑,而总想在项目中写一些犀利的代码,让人看起来很NB,但结果总是和想象差太远,代码总是写的很差,逻辑也不够清晰。归根到底,是我带着这样的思想去写代码,而忽略了编程的根本:解决问题。现在我改掉了这个坏毛病,以解决问题为目的去编程,以简单为主。出乎意料的是别人有时会对我说,这里的代码写得很棒。

  踏实的做事,会有意想不到的收获。

   承认错误

  不要怀疑,当别人用自己的程序或者代码无法运行时,首先考虑是否是自己的逻辑哪里有问题。一来别人会觉得我谦虚,二来实际大多数情况的确是自己的问题。

   有原则,有决心

  做任何事情都坚持原则,并有决心是最好的。有很多道理我们都明白,但经常做不到,没有任何人能帮到自己,未来也是自己争取的。

  所以,如果知道什么是好,就尽量去做,什么是不好,就尽量避免。

  即使是在公司面对经理和领导,也要坚持自己的做法,一些不合理的需求应该指出或拒绝。我还年轻,大不了换一家公司,而不愿意做一个受欺压的码农。

   我在做什么

  文章写完了,现在来回想一下,我是在分享自己现在编程的一些习惯,总算没偏离开始的主题。本文的思想都是来自实际工作和一些书籍,想了解更多的话,推荐阅读《整洁代码之道》《代码大全》《重构》这几本书。

  如果你有一些认为好的编程方法,不妨拿出来和大家分享一下。

相关 [编程] 推荐:

Hadoop Streaming 编程

- - 学着站在巨人的肩膀上
Hadoop Streaming是Hadoop提供的一个编程工具,它允许用户使用任何可执行文件或者脚本文件作为Mapper和Reducer,例如:. 采用shell脚本语言中的一些命令作为mapper和reducer(cat作为mapper,wc作为reducer). 本文安排如下,第二节介绍Hadoop Streaming的原理,第三节介绍Hadoop Streaming的使用方法,第四节介绍Hadoop Streaming的程序编写方法,在这一节中,用C++、C、shell脚本 和python实现了WordCount作业,第五节总结了常见的问题.

Shell编程

- - 博客园_首页
本来打算寒假回家好好学习Linux的,为以后学习嵌入式打好基础的. 回家之后的学习效率非常低,之前为了搭建Linux环境,折腾了很长时间,学到现在也就勉强才把Shell编程学完了. 今天就把自己学习的相关知识点总结整理一下. 个人感觉shell程序跟windows下的批处理文件有点像,就是将一些系统命令写进一个可执行文件中,然后执行.

用 AlphaCode 编程

- - 奇客Solidot–传递最新科技情报
至少在部分问题上 AI 程序员能与真正的程序员竞争了. Alphabet 旗下 AI 子公司 DeepMind 宣布了 AI 代码生成系统 AlphaCode(PDF),声称测试显示其水平在编程竞赛中已经具备了竞争力. 计算机科学家 Scott Aaronson 也为 AI 在编程方面的进步 惊叹不已.

编程能力与编程年龄

- - 酷壳 - CoolShell.cn
程序员这个职业究竟可以干多少年,在中国这片神奇的土地上,很多人都说只能干到30岁,然后就需要转型,就像《 程序员技术练级攻略》这篇文章很多人回复到这种玩法会玩死人的一样. 我在很多面试中,问到应聘者未来的规划都能听到好些应聘都说程序员是个青春饭. 因为,大多数程序员都认为,编程这个事只能干到30岁,最多35岁吧.

[译]所有编程皆为Web编程

- - 呦呦鹿鸣
原文作者:Jeff Atwood. Michael Braude对Web编程大受追捧表达了他的不屑:. 大部分人想去做Web编程的原因是,他们不够聪明,因此也做不了别的事. 他们不懂编译器、并发性、3D或类继承. 他们根本不明白我为什么要使用接口或者抽象类. 他们不理解虚函数、指针、引用、垃圾回收、终结器、传引用与传值的区别、C++的虚拟析构函数、或者C#的结构体与类之间的差别.

javascript 编程规范

- 红茶 - 博客园-Ruby's Louvre
为公司起草的javascript编程规范,参考了网上的许多资料,尤其是google的规范. 现在放出来,希望能抛砖引玉,大家多提宝贵意见. 本规范是针对javascript函数式编程风格与公司严重依赖于jQuery进行编码的现实制定出来. 禁止使用eval,with与caller(ecma262 v5 的use strict要求).

Javascript编程风格

- - 阮一峰的网络日志
Douglas Crockford是Javascript权威, Json格式就是他的发明. 去年11月他有一个演讲( Youtube),谈到了好的Javascript编程风格是什么. 我非常推荐这个演讲,它不仅有助于学习Javascript,而且能让你心情舒畅,因为Crockford讲得很幽默,时不时让听众会心一笑.

编程的未来

- - ITeye博客
最近在看一本书,加来道雄(Michio Kaku)的《物理学的未来》,第一、第二章是程序员更加关心的,对于下一个100年计算机和人工智能未来的预测. 想想计算机发展短暂的历史,这些发生了的翻天覆地的变化,似乎都在弹指一挥间. 无论如何,书中对其这样几个猜想令我记忆深刻:. 这是物理学家眼中的世界(另外推荐他的另一本书《平行宇宙》),激动人心;另一方面,我回想起小时候无比痴迷的机器猫,小小四维空间袋,寄托了孩子多少纯真的梦想,有多少神奇的道具已经成为现实…….

MapReduce编程模型

- - CSDN博客云计算推荐文章
MapReduce是一个Google发明的编程模型,也是一个处理和生成超大规模数据集的算法模型的相关实现. 用户首先创建一个Map函数处理一个基于对的数据集合,输出的中间结果基于对的数据集合,然后再创建一个Reduce函数用来合并所有的具有相同中间Key值的中间Value值.

Kafka编程实例

- - CSDN博客云计算推荐文章
    Producer是一个应用程序,它创建消息并发送它们到Kafka broker中. 这些producer在本质上是不同. 比如,前端应用程序,后端服务,代理服务,适配器对于潜在的系统,Hadoop对于的Producer. 这些不同的Producer能够使用不同的语言实现,比如java、C和Python.