英文原文:Give your code some space!
最近在几个不同的团队中发现他们的编码规范中对空格的用法大不相同。这激起了我的好奇心,因为我一直觉得,对于代码格式中的空格的使用,应该有一个大家都公认的最好的习惯。但是在现实开发中,似乎没有出现这样的统一。
首先,我们看看在大多数Java IDE中Java代码的风格习惯。
下面是一个例子:
public class Example { public static void main(String[] args) { int answer = 2 + 4 * 6; for (int i = 0; i < 5; i++) { doSomething(); } System.out.println("The answer is " + answer); } private static void doSomething() { // something } }
请注意,这是Java开发者中和其它类似C语言的语言中最常见的一种风格。
在括号内部两边没有留空格。只有当括号前面有关键字for
或while
等时,空格才会出现在它们之间,但在方法名和其之后的括号间没有空格。然而,在左大括号前总会有空格(这个左大括号一般会出现在一行的最后)。数学操作符总有空格包围。而分号前不会有空格。
如果你经常阅读代码,也许认为这是很自然的安排,但如果你不是一个程序员,可能会认为这些规则太随意,有很多不统一、特例、有待讨论的地方。
在我当前的开发项目中,公司里有两个主要的开发团队,一个是Load Test Products(LTP)团队,另一个是Functional Test Products(FTP)团队。两个团队里遵守了一些编码风格习惯,其中对空格的使用都是特别慷慨,就像下面这个代码片段的例子:
public class Example { public static void main( String[] args ) { int answer = 2 + 4 * 6; for( int i = 0; i < 5; i++ ) { doSomething(); } System.out.println( "The answer is " + answer ); } private static void doSomething() { // something } }
这种风格看起来代码的密度更小,而且更有统一性(例如,在左括号前都没有空格,在所有的括号、花括号、中括号的左右内边都有空格)。我并不是说这样比之前的风格更好或更差….只是这样更容易区别单词。代价就是一个屏幕里只能显示更少的代码。
虽然每种编码风格都有各自的优势和缺点,但让我吃惊的是,FTP开发团队最终决定转向使用之前提到的最常见的那种编码风格。他们认为这种转变是值得的,他们甚至还进行过讨论,所以,我相信他们认为这种转变是重要的。他们判断的依据并不是这种比那种好…我认为这主要着眼于什么人习惯这种风格,因为我们的大部分产品都是开源的,这是重要原因,更容易让社区内的程序员捐赠代码。
在我的团队里,LTP团队,我们仍然坚持我们的风格,不打算做任何改变。至少对于我来说,这是一种基于技术考虑的决定:如果另一种编码风格没有体现出任何的优势(我们这个项目对开源捐赠的需求很少),为什么要转向另一种风格?
我一直在给开源项目捐赠代码,比如Ceylon编程语言,让我非常吃惊的是,他们完全没有遵守任何的编码风格。你可以用任何你喜欢的风格来写代码。
也许某些被一些公司里大量的编码规范惊吓住的程序员会喜欢这样,但我相信,如果没一套编码规范,你的项目的代码很可能在格式上出现混乱。每个代码捐赠者都会在提交的代码里带入他特有的代码风格,这不奇怪,结果就是所有的代码各式各样,你从下面几行代码里就能看到多种不同的风格:
while (exists cell = iter) { if (exists elem = cell.element, elem==element) { last = cell; } iter = cell.rest; } } if (exists cell=last){ cell.element=replacement; return true; } assert (0<=index
我主要想提出的问题是:编码风格究竟是不是一种重要的开发制度?它对程序员开发软件的效率有多大影响?
我相信这个问题牵连到另外一个相似的问题:代码的格式影响代码的可读性吗?
我认为,这个问题非常容易回答!当然会影响。用眼睛逐行扫描一下下面两段代码(从Ceylon项目中选出的),你会很容易感觉到哪个更易读。 – CeylonCreate)?
function validModuleNameChar(Character c)=>c.letter||c.digit||c in ['_','.' ]; if (!trimmedName.empty, validModuleNameFirstChar(trimmedName.first else 'X'), trimmedName.every(validModuleNameChar), !(trimmedName.split('.'.equals,true,false)).containsAny(ceylonKeywords.chain {""})) { return trimmedName; } function validModuleNameChar( Character c ) => c.letter || c.digit || c in ['_', '.' ]; if ( ! trimmedName.empty, validModuleNameFirstChar ( trimmedName.first else 'X' ), trimmedName.every ( validModuleNameChar ), ! ( trimmedName.split ( '.'.equals, true, false ) ).containsAny ( ceylonKeywords.chain { "" } ) ) { return trimmedName; }
它们的区别也许很小,但如果你整天花大量的时间读大量的代码,这区别就大了。
说起代码的可读性,我们也许应该看看人们更普通的阅读文字的能力,这种能力已经发展了几千年,所以,应该会总结出来更好的方案!?
在“普通”的英文中,你可以看出约定俗成的对于空格的用法,而你却看不到作家们讨论他们是否应该在括号周边使用空格的问题!
那么,如果代码像“普通”英文那样编写,会是什么样子?
回到我们上面简单的例子:
function validModuleNameChar( Character c ) => c.letter || c.digit || c in ['_', '.' ]; if ( ! trimmedName.empty, validModuleNameFirstChar ( trimmedName.first else 'X' ), trimmedName.every ( validModuleNameChar ), ! ( trimmedName.split ( '.'.equals, true, false ) ).containsAny ( ceylonKeywords.chain { "" } ) ) { return trimmedName; }
代码里大部分的东西都本普通英文句子一样。你使用括号将某些字词“分组”。你在单词和符号之间使用空格——这里我们发现了一个跟普通编码习惯不一样的地方:我们在调用方法时,方法名和之后的括号间没有空格,对象点操作符后面不留空白。
我可以理解为什么在这些地方没有空格,因为代码里这些括弧或点号表示了一种“拥有”关系,或关联关系。但真的有必要挤压这一点空间来表示这种关系吗?当我们看上面的这个例子代码时不是感觉很清晰吗?
另外一个在普通英文里不存在的东西是嵌套表达…所以,我们有普通英文写作里不会出现的情况。但这不是大问题,只需要在记住一个通用规则:在符号两边加空格。
你是不是感觉上面的代码有些奇怪?我不得不承认,看起来稍微有些不舒服,当我相信这只是我还不习惯。
虽然还不习惯,但我确定这些代码读起来更容易理解。
我可以明确的说,如果所有的代码都遵守这样的习惯,那我们程序员阅读代码的过程将会轻松的多。
我希望没有程序员会认为遵守一个预先定义的编码规范会让编程变得困难!
首先,所有的IDE都能配置成自动对代码进行格式规范调整,你写代码时可以写的很乱,但最后别忘了使用IDE里的快捷键整理一下代码,然后再提交代码。同样,我估计没有哪个作家会认为我们的写文字的格式太严,限制了他的创作。
幸运的是,我发现当前所有的编码风格、规范里对空格的使用习惯都没有大问题。但多使用一些空格肯定会让代码更清晰易读。
你觉得呢?