`
ronghao
  • 浏览: 448416 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
E9473dd5-1985-3883-ac98-962354ca10b3
张小庆,在路上
浏览量:8528
社区版块
存档分类
最新评论

心理学,再谈好代码

阅读更多
什么代码才是好代码?这真是个老得能拔掉牙齿的话题。好吧,那让我们再在这刮沙尘暴的无聊时光里重复一次。好的代码要是易读的代码、要做到职责分离、要做到单一职责、要有高的执行效率....

等等,等等,这才抽象了,太书面化了。我只是一个菜鸟,刚写代码几年,也没念过什么书,能不能说得通俗易懂一些?

好吧,我停下来,想,这真是个难缠的家伙。我说,这样吧,我推荐几本书你去看吧,《重构》熊节最近再版了,建议你去买一本。恩,等等,有个省钱的招,去图灵俱乐部讨论组注册下,蹭赠书也很爽,哈哈。

可是,你还没告诉我什么代码才是好代码呢?知道你也没什么好答案,我自己来说好了。

省略掉此时我内心花花的汗水,下面是菜鸟的叙述:

1.一致
我发现自己有轻微的强迫症,当我碰到以下代码时,我就会冲动。
冲动前代码:
def imgName
if(XXX){
   imgName="meigui"
}else{
   imgName=""
}

冲动后代码:
def imgName=XXX?"meigui":""


尽管两段代码功能一致,但一旦我发现出现冲动前代码时,我就会感到不舒服,感到难受,就好像看到阅兵正步走不齐一样。方法名也是一样:
冲动前:
def testXXX(){}

冲动后:
def should_XXX_when_XXX(){}


变量亦是如此:
冲动前:
def imgNode=resouce.adoptTo(Node)

冲动后:
def node=resouce.adoptTo(Node)


总之,我不愿意看到同一个事情有两种实现方式,如果功能类似,那么不管是逻辑还是变量、方法名,我会强迫一致,整齐划一。

关于一致,从调试代码的角度看,零星的不一致比大量的不一致更加糟糕,因为这时大部分地方的一致性会令人麻痹大意。在实现查询分页功能时,我们有这样一行代码:
nodeIterator.size

这行代码的意思是获取查询结果的总数,大部分情况下它工作良好,但是在一种特殊情况下它返回了-1。这对我当时几乎是灾难性的,因为调试过程中我们始终相信这行代码的行为一致,结果是花费了一个下午才找到这个问题。

2.简洁
我喜欢短的代码,对我而言,短的程序总是比更长一些的代码容易理解,小学时学课文就已经这样了,一看到大段的段落我总是会晕过去(特别是文言文,首先我就对自己理解这段文字失去了信心)。这里要提到注释,即是这些注释明确是为了提高代码的可读性,也会增加我阅读代码的困难,所以我不会在方法里的任何位置添加注释,撑死在个别方法声明前添加,并且这种情况也尽量避免,如果这个类确实包含了重要的不易理解的算法,我也只会在类声明前添加注释。

关于自然语言,有一个基于经验的结论被称为Zipf定律,即:自然语言中最常用到的单词,其长度会趋于最短。

我写代码的时候,能够简写尽量简写,例如,变量名,imageNode,我一定会写成imgNode;方法名procedureXXX,我一定会写成procXXX,和讨厌大段代码一样,我非常讨厌命名很长的方法名和变量名,尽管这些名称这么长是为了更好的增加可读性,但可读性不是这样增加的。

在我的第一份代码工作里,我们使用拼音来命名方法和变量(还好,没有包括类名),我讨厌这种命名方式的原因并不是因为我的语文老师不好以至于我前后鼻音不分,而是这种写法根本排除了简写的可能性,甚至,为了避免歧义,有时不得不变得更长。

3.联觉和顺序
关于记忆,人类有两种重要的记忆能力:联觉和顺序记忆。

关于联觉,一个例子是:你总可以一眼记住一个人的脸,比如范冰冰,尽管我到现在也不清楚她到底是单眼皮还是双眼皮,也不清楚她到底是厚嘴唇还是薄嘴唇。

那么,在代码里,这里的表现就是局部,即一个功能的所有相关代码都集中在一个地方。我最讨厌的代码是这样的:最开始我打开一个文件,在阅读的过程中,我发现一个不清楚的方法,于是我按下ctrl并点击鼠标,于是我跳到另外一个文件;接下来,在阅读另外一个方法里,我再次发现了一个不清楚的方法,于是我再次按下ctrl并点击鼠标,哇哈,新的文件打开了....如此反复,终于当我打开最后一个文件时,我发现IDE的文件条里已经密密麻麻的排满了好几排文件,于是,我移动鼠标,右键,弹出一个关闭菜单,我选择了close others,瞬间,哦米拖佛,整个世界清静了,但是,等等,我最初是打算干嘛来着?

所以,请把所有相关联的代码都集中在一个地方,求您了。哦,对了,能不用接口请不要用接口,总会碰到这样的情况,打开好几排的文件,接口文件占了一半,我靠,少几个接口会死啊。对了,这可能是您的一致性心理在作怪,对不起,对不起。

关于局部,一个范例同样与调试有关,在很久之前的一次调试中,我们始终找不到一个变量错误的原因,因为在这段代码里,根本找不到任何错误,很久以后,终于发现,这个变量竟然是个全局变量,嘿嘿,告诉你吧,这个变量在servlet里,04年的时候,网上很火的一篇文章,标题就是:不要在servlet里使用全局变量!

关于顺序,最典型的例子出现在高中化学里,我总也不能瞬间说出第12、13个化学元素是什么,我通常会这样记忆:氢氦锂铍硼碳氮氧氟氖钠镁铝硅磷,啊哈,第12个元素是镁,第13个元素时铝,合起来就是--美女!

所以,在代码里,请将互相调用的方法按顺序摆放,方法1先调用了方法2,那么请将方法2紧放在方法1后边。我讨厌这样的配置:打开方法1,发现其调用了方法2,点击方法2,编辑器里的滚动条瞬间从最上端滚到最下端,紧接着,滚动条又从最下端滚动到中间,再接着,又是最下端,接着,归零到最上端....人生经不起这样的大起大落,真的,那得要多么大的心脏啊,麦蒂才有过那么一次,13秒....

还有,知道为什么goto为什么那么臭名昭著了吧。

4.自然
使得代码具有轻松的表达方式,同时把错误率降到最低,一种最重要的方法就是代码变得“自然”,即向自然语言靠拢。因为代码并不仅仅是与机器交流的,更重要的是,需要在人之间交流。

机器语言到高级语言,面向过程语言到面向对象语言,jdbc到hibernate,java到动态语言,这些都促使代码变得更加自然。

Ruby里有个不起眼的特性,就是方法调用不用再写括号,这一特性是如此的微不足道但是却被很多人津津乐道,原因就是它更加自然,更加贴近我们的自然语言。于是,我看到,我的同事晓娜,在groovy里,一遍遍的将她力所能及的括号去掉。

此外,程序语言和自然语言是有区别的,除了不能在代码里利用感情词抒发情感之外(我想,如果可以,一定会看到很多的冯特),程序语言没有口语。很少看到程序员之间这样交流,来吧,我们来说段代码(当然也有,徐昊就可以,哈哈),他们更多的会使用白板和笔或者直接是编辑器。所以,结束招聘时是否需要笔试的争论吧,我真为那些不经过笔试就直接招人的公司感到羞愧,因为他们根本就不懂程序语言。

此处省略华丽的分割线。

此文谢谢我们项目组的激烈讨论,谢谢讨论中徐昊的精彩点评。

Hi,各位,我想说明一下这篇帖子的目的,这里并没有给什么是好代码下一个明确的定义,这里只是一个观察的角度,提供另外一个视角,仅此而已。温伯格说过,所有系统都是观察者的一种主观幻觉而已,大概就是这个意思。
分享到:
评论
59 楼 gtssgtss 2010-12-30  
如果是boolean表达式,我一般不用三元表达式
如果是boolean值,我就用三元表达式
58 楼 alexhobo 2010-12-29  
抛出异常的爱 写道
hotjava 写道
抛出异常的爱 写道
hotjava 写道
hotjava 写道
黑暗浪子 写道
hotjava 写道
冲动后代码:
Java代码
def imgName=XXX?"meigui":"" 

---------
这样的代码是垃圾。

讲出道理来,为什么是垃圾?这里不是让你乱说话的。


---------------------------------------
1、代码可读性查,7,8个人改过以后我看你怎么维护。
2、扩展性差,结构性不强,内聚性不强,不方便抽象。
3、如果你是需要像写SQL一样必须写道一行里面,比如DECODE,那的确是没办法,但是IDE里这么大版面你干吗要把代码写成太空文一样的一长串?
4、基于以上原因,我认为这样的代码是垃圾,我定义的编码规范里面没有特殊原因是不可以用的。
5、请解释“这里不是让你乱说话的。”是什么意思?



补充一点,DEBUG的时候,真是想死,想把写这种代码的拖出去杀了,另外黑暗浪子大哥,强烈要求解释下“这里不是让你乱说话的。”是什么意思?


DECODE 比case排版好看点外没发现什么好处。
对于TDD的狂热者大约IDE上不需要DEBUG。。。。(我还不算是,但也是很少才会用到的)
不让乱说话是指一些无聊的人会有大师崇拜症。(大师的话一定是对的 )
他们不容别人提反对意见,
说错话
会被小白们
送小黑屋
作脑残小测试



1、对于DECODE,我的意见是简单的场景可以使用,比如null转化成0。
2、本人有DEBUG综合症,代码不读,直接DEBUG,DEBUG的过程中理解程序含义。
3、我本人非计算机出身,纯野路子,计算机大师一个也不认识。TDD也不懂,领导也不让用。所以比较郁闷
4、我对代码的要求就是,a、好读,b好改,c好测。写代码就根写文章(文档)一样,先分清段落大意,段落与段落之间的关系,再把段落中包含的内容按顺序列举,列好小标题,然后层层扩展。最后在抽象整理一下。
5、一个人的精力实在有限,很难要求所有人都能把代码统一,做IT的太累了。

nvl??
崇拜一下记忆力可压栈的人类。
大约是老了看2K行代码吃力的很
当然统一风格很吃力
如果有踢回重写的权力
还好一点

抛出异常的爱 写道
hotjava 写道
抛出异常的爱 写道
hotjava 写道
hotjava 写道
黑暗浪子 写道
hotjava 写道
冲动后代码:
Java代码
def imgName=XXX?"meigui":"" 

---------
这样的代码是垃圾。

讲出道理来,为什么是垃圾?这里不是让你乱说话的。


---------------------------------------
1、代码可读性查,7,8个人改过以后我看你怎么维护。
2、扩展性差,结构性不强,内聚性不强,不方便抽象。
3、如果你是需要像写SQL一样必须写道一行里面,比如DECODE,那的确是没办法,但是IDE里这么大版面你干吗要把代码写成太空文一样的一长串?
4、基于以上原因,我认为这样的代码是垃圾,我定义的编码规范里面没有特殊原因是不可以用的。
5、请解释“这里不是让你乱说话的。”是什么意思?



补充一点,DEBUG的时候,真是想死,想把写这种代码的拖出去杀了,另外黑暗浪子大哥,强烈要求解释下“这里不是让你乱说话的。”是什么意思?


DECODE 比case排版好看点外没发现什么好处。
对于TDD的狂热者大约IDE上不需要DEBUG。。。。(我还不算是,但也是很少才会用到的)
不让乱说话是指一些无聊的人会有大师崇拜症。(大师的话一定是对的 )
他们不容别人提反对意见,
说错话
会被小白们
送小黑屋
作脑残小测试



1、对于DECODE,我的意见是简单的场景可以使用,比如null转化成0。
2、本人有DEBUG综合症,代码不读,直接DEBUG,DEBUG的过程中理解程序含义。
3、我本人非计算机出身,纯野路子,计算机大师一个也不认识。TDD也不懂,领导也不让用。所以比较郁闷
4、我对代码的要求就是,a、好读,b好改,c好测。写代码就根写文章(文档)一样,先分清段落大意,段落与段落之间的关系,再把段落中包含的内容按顺序列举,列好小标题,然后层层扩展。最后在抽象整理一下。
5、一个人的精力实在有限,很难要求所有人都能把代码统一,做IT的太累了。

nvl??
崇拜一下记忆力可压栈的人类。
大约是老了看2K行代码吃力的很
当然统一风格很吃力
如果有踢回重写的权力
还好一点

无论哪种方式都不是绝对的好或不好
风格的统一才是好的
三元表达式 也好 命名缩写也好
只代表一种风格
宁愿错误的一致 也不要对错的交织
57 楼 抛出异常的爱 2010-04-24  
hotjava 写道
抛出异常的爱 写道
hotjava 写道
hotjava 写道
黑暗浪子 写道
hotjava 写道
冲动后代码:
Java代码
def imgName=XXX?"meigui":"" 

---------
这样的代码是垃圾。

讲出道理来,为什么是垃圾?这里不是让你乱说话的。


---------------------------------------
1、代码可读性查,7,8个人改过以后我看你怎么维护。
2、扩展性差,结构性不强,内聚性不强,不方便抽象。
3、如果你是需要像写SQL一样必须写道一行里面,比如DECODE,那的确是没办法,但是IDE里这么大版面你干吗要把代码写成太空文一样的一长串?
4、基于以上原因,我认为这样的代码是垃圾,我定义的编码规范里面没有特殊原因是不可以用的。
5、请解释“这里不是让你乱说话的。”是什么意思?



补充一点,DEBUG的时候,真是想死,想把写这种代码的拖出去杀了,另外黑暗浪子大哥,强烈要求解释下“这里不是让你乱说话的。”是什么意思?


DECODE 比case排版好看点外没发现什么好处。
对于TDD的狂热者大约IDE上不需要DEBUG。。。。(我还不算是,但也是很少才会用到的)
不让乱说话是指一些无聊的人会有大师崇拜症。(大师的话一定是对的 )
他们不容别人提反对意见,
说错话
会被小白们
送小黑屋
作脑残小测试



1、对于DECODE,我的意见是简单的场景可以使用,比如null转化成0。
2、本人有DEBUG综合症,代码不读,直接DEBUG,DEBUG的过程中理解程序含义。
3、我本人非计算机出身,纯野路子,计算机大师一个也不认识。TDD也不懂,领导也不让用。所以比较郁闷
4、我对代码的要求就是,a、好读,b好改,c好测。写代码就根写文章(文档)一样,先分清段落大意,段落与段落之间的关系,再把段落中包含的内容按顺序列举,列好小标题,然后层层扩展。最后在抽象整理一下。
5、一个人的精力实在有限,很难要求所有人都能把代码统一,做IT的太累了。

nvl??
崇拜一下记忆力可压栈的人类。
大约是老了看2K行代码吃力的很
当然统一风格很吃力
如果有踢回重写的权力
还好一点
56 楼 hotjava 2010-04-24  
抛出异常的爱 写道
hotjava 写道
hotjava 写道
黑暗浪子 写道
hotjava 写道
冲动后代码:
Java代码
def imgName=XXX?"meigui":"" 

---------
这样的代码是垃圾。

讲出道理来,为什么是垃圾?这里不是让你乱说话的。


---------------------------------------
1、代码可读性查,7,8个人改过以后我看你怎么维护。
2、扩展性差,结构性不强,内聚性不强,不方便抽象。
3、如果你是需要像写SQL一样必须写道一行里面,比如DECODE,那的确是没办法,但是IDE里这么大版面你干吗要把代码写成太空文一样的一长串?
4、基于以上原因,我认为这样的代码是垃圾,我定义的编码规范里面没有特殊原因是不可以用的。
5、请解释“这里不是让你乱说话的。”是什么意思?



补充一点,DEBUG的时候,真是想死,想把写这种代码的拖出去杀了,另外黑暗浪子大哥,强烈要求解释下“这里不是让你乱说话的。”是什么意思?


DECODE 比case排版好看点外没发现什么好处。
对于TDD的狂热者大约IDE上不需要DEBUG。。。。(我还不算是,但也是很少才会用到的)
不让乱说话是指一些无聊的人会有大师崇拜症。(大师的话一定是对的 )
他们不容别人提反对意见,
说错话
会被小白们
送小黑屋
作脑残小测试



1、对于DECODE,我的意见是简单的场景可以使用,比如null转化成0。
2、本人有DEBUG综合症,代码不读,直接DEBUG,DEBUG的过程中理解程序含义。
3、我本人非计算机出身,纯野路子,计算机大师一个也不认识。TDD也不懂,领导也不让用。所以比较郁闷
4、我对代码的要求就是,a、好读,b好改,c好测。写代码就根写文章(文档)一样,先分清段落大意,段落与段落之间的关系,再把段落中包含的内容按顺序列举,列好小标题,然后层层扩展。最后在抽象整理一下。
5、一个人的精力实在有限,很难要求所有人都能把代码统一,做IT的太累了。
55 楼 抛出异常的爱 2010-04-24  
hotjava 写道
hotjava 写道
黑暗浪子 写道
hotjava 写道
冲动后代码:
Java代码
def imgName=XXX?"meigui":"" 

---------
这样的代码是垃圾。

讲出道理来,为什么是垃圾?这里不是让你乱说话的。


---------------------------------------
1、代码可读性查,7,8个人改过以后我看你怎么维护。
2、扩展性差,结构性不强,内聚性不强,不方便抽象。
3、如果你是需要像写SQL一样必须写道一行里面,比如DECODE,那的确是没办法,但是IDE里这么大版面你干吗要把代码写成太空文一样的一长串?
4、基于以上原因,我认为这样的代码是垃圾,我定义的编码规范里面没有特殊原因是不可以用的。
5、请解释“这里不是让你乱说话的。”是什么意思?



补充一点,DEBUG的时候,真是想死,想把写这种代码的拖出去杀了,另外黑暗浪子大哥,强烈要求解释下“这里不是让你乱说话的。”是什么意思?


DECODE 比case排版好看点外没发现什么好处。
对于TDD的狂热者大约IDE上不需要DEBUG。。。。(我还不算是,但也是很少才会用到的)
不让乱说话是指一些无聊的人会有大师崇拜症。(大师的话一定是对的 )
他们不容别人提反对意见,
说错话
会被小白们
送小黑屋
作脑残小测试
54 楼 hotjava 2010-04-22  
hotjava 写道
黑暗浪子 写道
hotjava 写道
冲动后代码:
Java代码
def imgName=XXX?"meigui":"" 

---------
这样的代码是垃圾。

讲出道理来,为什么是垃圾?这里不是让你乱说话的。


---------------------------------------
1、代码可读性查,7,8个人改过以后我看你怎么维护。
2、扩展性差,结构性不强,内聚性不强,不方便抽象。
3、如果你是需要像写SQL一样必须写道一行里面,比如DECODE,那的确是没办法,但是IDE里这么大版面你干吗要把代码写成太空文一样的一长串?
4、基于以上原因,我认为这样的代码是垃圾,我定义的编码规范里面没有特殊原因是不可以用的。
5、请解释“这里不是让你乱说话的。”是什么意思?



补充一点,DEBUG的时候,真是想死,想把写这种代码的拖出去杀了,另外黑暗浪子大哥,强烈要求解释下“这里不是让你乱说话的。”是什么意思?
53 楼 hotjava 2010-04-22  
黑暗浪子 写道
hotjava 写道
冲动后代码:
Java代码
def imgName=XXX?"meigui":"" 

---------
这样的代码是垃圾。

讲出道理来,为什么是垃圾?这里不是让你乱说话的。


---------------------------------------
1、代码可读性查,7,8个人改过以后我看你怎么维护。
2、扩展性差,结构性不强,内聚性不强,不方便抽象。
3、如果你是需要像写SQL一样必须写道一行里面,比如DECODE,那的确是没办法,但是IDE里这么大版面你干吗要把代码写成太空文一样的一长串?
4、基于以上原因,我认为这样的代码是垃圾,我定义的编码规范里面没有特殊原因是不可以用的。
5、请解释“这里不是让你乱说话的。”是什么意思?
52 楼 抛出异常的爱 2010-04-22  
coolyzg 写道
LZ是百把人公司的老总,主要关注市场与营销,还在琢磨代码啊

http://www.iteye.com/topic/572391


toughtwok 的 马丁  也天天 琢磨代码
51 楼 coolyzg 2010-04-22  
LZ是百把人公司的老总,主要关注市场与营销,还在琢磨代码啊

http://www.iteye.com/topic/572391
50 楼 maxiaoxia 2010-04-16  
黑暗浪子 写道
抛出异常的爱 写道
ThinkingInAll 写道
碰到if else我会先想一下能不能做成多态

变态

反对~~

我也觉得变态
49 楼 黑暗浪子 2010-04-16  
抛出异常的爱 写道
ThinkingInAll 写道
碰到if else我会先想一下能不能做成多态

变态

反对~~
48 楼 抛出异常的爱 2010-04-16  
ThinkingInAll 写道
碰到if else我会先想一下能不能做成多态

变态
47 楼 poson 2010-04-16  
dreamhead 写道
推荐你看一下《Clean Code》(中文名《代码整洁之道》)。

imageNode变成imgNode不见得是好的选择,前提是全项目组就这个缩写达成一致。比如,procXXX究竟是procedureXXX还是processXXX呢。如果能短固然好,但更要的是表意。


clean code很不错的书。感觉比《重构》那本书还要好一些。
46 楼 ThinkingInAll 2010-04-06  
碰到if else我会先想一下能不能做成多态
45 楼 黑暗浪子 2010-04-04  
抛出异常的爱 写道
黑暗浪子 写道
抛出异常的爱 写道
强强爱妍妍 写道
抛出异常的爱 写道
public Object kk(){
		return zz()?tt()?aa():bb():kk();
	}


这个绝对是坏习惯. 函数返回值要用变量保存起来再使用.

这个式子可以无限延长。。。。
看起来很美。。。。
真正有问题。
DEBUG时想要杀人。

PS:你如果发现这里面含有一个底归。。。。
你就知道为什么我看到这代码时会抓狂。。。。

这年头谁还用递归?谁还用内部类?谁还用匿名类?谁还用switch?谁还用goto?

几乎这几年都见过。。。。
而且为数不少。

PS:goto退化成break后还是有人爱用。。。。

碰到这样的人,我就会问:做JAVA几年了?
44 楼 抛出异常的爱 2010-04-04  
黑暗浪子 写道
抛出异常的爱 写道
强强爱妍妍 写道
抛出异常的爱 写道
public Object kk(){
		return zz()?tt()?aa():bb():kk();
	}


这个绝对是坏习惯. 函数返回值要用变量保存起来再使用.

这个式子可以无限延长。。。。
看起来很美。。。。
真正有问题。
DEBUG时想要杀人。

PS:你如果发现这里面含有一个底归。。。。
你就知道为什么我看到这代码时会抓狂。。。。

这年头谁还用递归?谁还用内部类?谁还用匿名类?谁还用switch?谁还用goto?

几乎这几年都见过。。。。
而且为数不少。

PS:goto退化成break后还是有人爱用。。。。
43 楼 ivyshark 2010-04-04  
改成您那样有的时候不一定合适 要看情况了 还是要和规范走
42 楼 黑暗浪子 2010-04-03  
抛出异常的爱 写道
强强爱妍妍 写道
抛出异常的爱 写道
public Object kk(){
		return zz()?tt()?aa():bb():kk();
	}


这个绝对是坏习惯. 函数返回值要用变量保存起来再使用.

这个式子可以无限延长。。。。
看起来很美。。。。
真正有问题。
DEBUG时想要杀人。

PS:你如果发现这里面含有一个底归。。。。
你就知道为什么我看到这代码时会抓狂。。。。

这年头谁还用递归?谁还用内部类?谁还用匿名类?谁还用switch?谁还用goto?
41 楼 抛出异常的爱 2010-04-03  
强强爱妍妍 写道
抛出异常的爱 写道
public Object kk(){
		return zz()?tt()?aa():bb():kk();
	}


这个绝对是坏习惯. 函数返回值要用变量保存起来再使用.

这个式子可以无限延长。。。。
看起来很美。。。。
真正有问题。
DEBUG时想要杀人。

PS:你如果发现这里面含有一个底归。。。。
你就知道为什么我看到这代码时会抓狂。。。。
40 楼 强强爱妍妍 2010-04-03  
抛出异常的爱 写道
public Object kk(){
		return zz()?tt()?aa():bb():kk();
	}


这个绝对是坏习惯. 函数返回值要用变量保存起来再使用.

相关推荐

Global site tag (gtag.js) - Google Analytics