The Good Code

回家在XP上写东西,惊喜的发现连国产软件也都开始嫌弃和逐渐放弃XP和IE了。恩,慢归慢,只要不倒车,车轮总还是向前走的。

技术总是在飞速的更新。但无论技术怎么革新、新概念如何吹嘘,一行行的源代码始终是整个软件生态系统中最重要的根基。就像猎命师靠收命格,海贼靠热血,绝地武士靠原力,写代码才是程序员安身立命之本。

写了差不多五年代码,多多少少能沉淀一点跟《Clean Code》和《Refactoring》不太一样的东西了。

一记:

注释

写的好的代码都是不用注释的——此乃谬误之一

反例如下:

foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f a bs =
   foldr (\b g x -> g (f x b)) id bs a

上面的代码写的好么?当然好,而且可以说是最正确的唯一解,各方面都无可挑剔。而且就算是我把a b c都换成param1 param2 returnVal,提高所谓的“可读性”,我猜就算是会写一点haskell的程序员也无法完全理解上面的代码。

不用写注释的代码只限于everyday-dumb-logic的代码,我猜,心智健全的程序员,应该也写不出类似

//loop foo array to do some stuff
foreach(var a in foo)

的代码把。

话说回来,如果要写注释,上面那段haskell代码最好的注释是什么?

-- https://wiki.haskell.org/Foldl_as_foldr

当然是能把问题说清楚的blog/wiki/stackoverflow的URL了!在代码里注释直接附上URL真的不失为一个好选择,复杂的问题(比如那些神奇的,如小学奥数一般的算法)三言两语就说不清楚,而且一般编程语言的注释也不会支持格式或者是图片。加个URL还相当于是对后来人授之以渔了,何乐而不为。

注释写的越长越好——此乃谬误之二

以正常人的文字水平来说,确实字用的多一点,有更大的概率把能事情说清楚。只是,代码是会变的。不管是主动重构也好,被动改bug也罢,代码又不是石碑上的镌文,总会有改的那天。代码改了,注释会跟着一起改么?大概率不会。一是改bug心急,急着验证结果;二是可能根本就不会注意到上面的一大段注释(可能都不是我写的,我管他)。

所以,在说清楚的前提下,注释还是字数少点为好。

改篇《荷塘月色》好过去改《荷马史诗》。

注释是写给人(别人和自己)看的——这不是谬误

我一开始注释总是写英文,后来都改成中文了。

RTFM

RTFM = Read The Fucking Manual,在此不对整个人类在有了互联网以后脾气越来越大这件事做过多的评论,单说读Manual,其实还是挺有必要的。

各种编程语言都有specification或者是wiki之类的东西,窃以为读读这些东西,远比《x天精通y》要有用的多。

举一例,说明Manual上推荐的写法对你的代码有益:

c#里有property,我们不用像java一样去写(或者生成)很多无聊的getter&setter,但就算是读微软的一些开源项目的源代码(其实包括BCL里的API),你都能发现有很多getXXXX的方法,为何不全用get-property搞定?

语义是一方面,另一方面是官方的spec里明确的建议 —— get-method(所有method)都是可能有消耗的,而get-property(也包括set-property)没有。

具体来说:

public int SomeProp
{
	get
	{
		DoSomeLongWaitingStuff();
		int result = QueryFromDB();
		return result;
	}
}

就是标准的编译正确但是实际错误的写法,如果用Fxcop之类代码扫描的工具,是很可能会发现这些问题的。

等等,为什么这代码是实际错误的?原因很简单:无论是本能也好读过规范也罢,我们都很有可能在使用foo.SomeProp的时候根本没有考虑到实际的消耗,不小心埋下了性能的大坑。

foreach(var a in arr)
{
	DoOtherStuff(foo.SomeProp);
}

是不是看起来一点问题都没有?而实际上在loop里多余的去访问DB或者做其他不必要的操作,在性能上是完全无法接受的。而改成method以后:

foreach(var a in arr)
{
	DoOtherStuff(foo.GetSomeProp());
}

多数人还是能察觉到有点问题的。

再说unity3d中的monobehavior中的public field,从语言上来说肯定是不合理的东西,但是unity3d只是把C#作为scripting,官方也都推荐这么做,设计如此

其实有个看起来很美的解决方案:需要且只需要在editor中修改的field用private+[Serializable];需要在editor和其他类中修改的field用public,虽然不是最OO,也算是尽量的OO了。只是,有必要么?

并没有。

haskell作为pure functional的语言,是怎么处理类似IO这种有side effect一点都不纯的问题的呢?

It turns out that Haskell actually has a really clever system for dealing with functions that have side-effects that neatly separates the part of our program that is pure and the part of our program that is impure, which does all the dirty work like talking to the keyboard and the screen.

Separtion!把两部分分开就好了——所有继承于unity3d的基类的代码,就当做scripting好了,不必拘泥于private/public之类的问题;而其他部分的代码,比如game logic(什么?你的logic都在update里?)等等,还是当成“正经的C#”来写,因为这部分代码,你是需要享受到诸如robust或者是portable之类的OOP的好的。

Read the fucking manual, know the fucking manual.

Bug Bug Bug

我们不生产bug,我们只是bug的搬运工

程序有bug太正常,怎么改bug却是技术活。是hotfix还是re-design全凭具体问题具体分析,在deadline上当然是hotfix最好,因为没有其他选择;但是如果在项目或者模块的初期,还是仔细理清楚bug之后隐藏的问题,考虑是不是最开始的设计就有问题为好。

很多bug是出在设计的问题。

有个不算具体的例子——相似的复杂的逻辑出现两次或更多,如果一个地方有bug,就算另一个地方代码不完全一样,也会有bug的隐患。怎么杜绝这种隐患,重构出一个private的方法就好了,当然不是那么简单的。

具体的例子几百行的code sample也不一定能说清楚。(其实我也没有想到合适的)

反正就是想强调:改bug还是不能全都hotfix,多想想前因后果,可能有各种好处。

说白了,今天麻烦一点点,只是为了能省去日后更多的麻烦。

Format

一开始,我是觉得代码的命名和格式要照着大牛的抄,才会变得厉害。

后来,我发现还是年轻。(是有益,但皮面功夫哪里抵得上内在)

代码格式就像审美水平,人各有异,要懂得尊重他人——打人不打脸,改人代码不改格式。

自己看起来爽,其他大部分人看起来不恶心就行。

Suppress Warning

想要Suppress警告,首先你得关注过警告。

warning是个挺重要的东西,旨在提醒你代码里那些潜在的风险。多关注一下warning,即使是stackoverflow一下这为啥是个warning都肯定对你有好处。而那些明确知道不是风险而又设计如此的部分,还是写个注释或者pragma来suppress一下为好。好几百个warning,强迫症不舒服在其次,最重要是影响心情打击积极性。

Naming

各种大牛都说过,写代码,最难的就是命名了。方法论和各种歪理邪说有太多了,我说一例——当你的代码里已经有类似 beginFight startBattle preStartProc fightBeginUtilXXX 的时候,想要精确的表达开战之前show出来的几个大字的特效的方法,最好的命名就是——ShowKaiZhanEffect

不管是咋整还是点解,看得懂的命名就是好命名——出自xxx语录

以上都是废话

劳资加班到10点,需求都写不完,哪有那么多时间关注那么多东西,写完就不错了,哪有时间管他good不good。

一次完美的投篮,需要关注好几十个细节,调整全身上下好几十块肌肉,为啥库里能在0.5s之内完成一次次逆天的三分投篮,不仅其他人准还比其他人快?因为他不是人

肌肉记忆!(我们靠技术吃饭的都信这个)

代码写的好,就会一直写的好并且写的更好,就算是时间有限也会有品质保证。没习惯没态度,光靠YY就能变强,现在起点爽文估计都不会走这个套路了。

重要的事情可以再强调一遍:写代码才是程序员安身立命之本

force