scala - 最佳实践(02) - 代码洁癖
代码洁癖
我们写代码给计算机运行,但是读代码的不仅仅是计算机,还有我们的战友(同事),还有未来的战友。
我们不能做一个猪队友,所以保证通用的代码规范是必要的。
每行代码需要有一个合理的长度
避免从左到右有很长的代码,当理解这行代码的时候会占用我们的思维。
在印刷制品中,最合理的长度在50-70个字符。
在编程过程中,我们使用缩进字符,60个字符太短了。
80字符通常是可以接受的,但不能用在Scala中,因为在Scala中我们使用了很多闭包函数和类的长名称和描述性名称,80字符太短了。
一种平衡的策略:
- 我们将80个字符作为基准。
- 一般不要超过100个字符。
- 如果可以的话将函数调用切换一行。
- 最好不要超过120个字符(IDEA默认提示120个字符)
不要依赖SBT或IDE插件来格式化代码
IDE会提供格式化插件,但使用的时候必须要小心。
IDE只是提供一个强制的规则,它并不能理解开发者的意图,简单的部分可以使用自动格式化。但还是尽量使用我们自己的逻辑添加格式。
一定要注意别调整他人已经仔细格式化好的代码!!
代码案例:
val dp = new DispatchPlan(new Set(filteredAssets), start = startDate, end = endDate, product, scheduleMap, availabilityMap, Set(activationIntervals.get), contractRepository, priceRepository)
大多数情况下,自动格式化会有如下结果:
val dp = new DispatchPlan(Set(filteredAssets), start = startDate, end = endDate, product, scheduleMap, availabilityMap, Set(activationIntervals), contractRepository, priceRepository)
上面的可读性并不好,我们可以有如下的格式:
val dp = new DispatchPlan( Set(filteredAssets), startDate, endDate, product, scheduleMap, availabilityMap, Set(activationIntervals), contractRepository, priceRepository )
上面这个看上去好多了,但事实上,对于某些场景,我们需要断开一行:
val result = service.something(param1, param2, param3, param4).map(transform)
如下两个糟糕的写法:
// transform调用不爽 val result = service.something( param1, param2, param3, param4).map(transform) // 打破的逻辑 val result = service.something( param1, param2, param3, param4 ).map(transform)
下面这个会好很多:
val result = service .something(param1, param2, param3, param4) .map(transform)
现在好多了,不是吗?当然,有时候这行太长了,您需要求助于某种临时值:
val result = { val instance = object.something( myAwesomeParam1, otherParam2, someSeriousParam3, anEvenMoreSoParam4, lonelyParam5, catchSomeFn6, startDate7 ) for (x <- instance) yield transform(x) }
- 当然,有时如果代码非常糟糕,就需要进行重构,比如,对于一个函数来说,参数太多。
- 所以我们严格控制每行代码的长度,避免使用自动格式化插件,会产生很多可读性的问题。
避免使用过长的函数
理想的函数应该只有几行。如果行太长,我们就需要把它们分解成更小的函数并给它们命名。
注意,在Scala中,我们不需要在其他范围中提供这样的中间函数,这里的目的主要是帮助可读性,所以在Scala中,我们可以使用内部函数将逻辑分解为多个部分。
避免错别字
多留意下划线提示吧! 避免英文拼写错误,和中文注释错别字等等。
声明有意义的名称
*“在计算机科学中只有两件难事:缓存失效和命名。” -- Phil Karlton
这里我们有三条指导原则:
- 给出描述性的名称,但不要太过分,四个单词已经有点太多了
- 如果类型/用途可以从直接上下文轻松推断出来,或者已经建立了一种约定,那么可以简洁地命名
- 如果是描述性的,不要说无意义的废话
如下这个例子是可以接受的:我们可以从语境中直接看出p是一people,所以一个简短的字母名就可以了。
for (p <- people) yield transformed(p)
如下这个例子是可以接受的:因为“i”是作为索引使用的一种惯例
for (i <- 0 until limit) yield ???
如下这通常是不可接受的,因为通常使用元组时,集合的命名不能很好地反映所包含的内容(如果没有为这些元素命名,那么集合本身就会有不好的名称):
someCollection.map(_._2)
如下隐式参数对于短名称来说是可以接受的,因为隐式传递时,我们并不关心它们,除非它们丢失了:
def query(id: Long)(implicit ec: ExecutionContext, c: WSClient): Future[Response]
这是不可接受的,因为这个名称完全没有意义,即使有明确的描述性尝试:
def processItems(people: Seq[Person]) = ???
这是不可接受的,因为这个函数的命名表明了一个副作用(“process”是一个动词,表示一个命令),但是它并没有描述我们对这些“人”所做的事情。“Items”后缀是没有意义的,因为我们可能会说“processThingy”、“processRows”、“processStuff”,但它仍然会说完全相同的东西——绝对没有。它还增加了视觉上的混乱,因为更多的单词就是更多的文本,而无意义的单词只是噪音