简洁代码的重要性:代码读的频率远高于写

Programs must be written for people to read, and only incidentally for machines to execute.

(程序必须书写得能够让人阅读,只是顺便能够让机器执行。)

———— Abelson & Sussman:《计算机程序的构造与解释》


你或许会问:代码真正“读”的成分有多少呢?难道力量主要不是用在“写”上吗?

读与写花费时间的比例超过10:1。写新代码时,我们一直在读旧代码。

既然比例如此之高,我们就想让读的过程变得轻松,即便那会使得编写过程更难。没可能光写不读,所以使之易读实际也使之易写。

这事概无例外。不读周边代码的话就没法写代码。编写代码的难度,取决于读周边代码的难度。要想干得快,要想早点做完,要想轻松写代码,先让代码易读吧。

———— Robert C. Martin《代码简洁之道》


One of Guido's key insights is that code is read much more often than it is written. A style guide is about consistency. Consistency with this style guide is important. Consistency within a project is more important. Consistency within one module or function is the most important.

(代码的读取频率远高于编写频率。 风格指南是关于一致性的。 与此风格指南的一致很重要。 项目的一致性更为重要。 一个模块或功能内的一致性是最重要的。)

———— PEP 8:Python 官方代码风格指南

什么是简洁的代码

简洁的代码都有共同的特点,简洁的代码最好是能够自解释的,当你读这些代码的时候就像读一篇文章一样。关于一些原则,比较推荐 Python 之禅 的描述:

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

Python之禅 by Tim Peters

优美胜于丑陋(Python 以编写优美的代码为目标)
明了胜于晦涩(优美的代码应当是明了的,命名规范,风格相似)
简洁胜于复杂(优美的代码应当是简洁的,不要有复杂的内部实现)
复杂胜于凌乱(如果复杂不可避免,那代码间也不能有难懂的关系,要保持接口简洁)
扁平胜于嵌套(优美的代码应当是扁平的,不能有太多的嵌套)
间隔胜于紧凑(优美的代码有适当的间隔,不要奢望一行代码解决问题)
可读性很重要(优美的代码是可读的)
即便假借特例的实用性之名,也不可违背这些规则(这些规则至高无上)
不要包容所有错误,除非你确定需要这样做(精准地捕获异常,不写 except:pass 风格的代码)
当存在多种可能,不要尝试去猜测
而是尽量找一种,最好是唯一一种明显的解决方案(如果不确定,就用穷举法)
虽然这并不容易,因为你不是 Python 之父(这里的 Dutch 是指 Guido 
做也许好过不做,但不假思索就动手还不如不做(动手之前要细思量)
如果你无法向人描述你的方案,那肯定不是一个好方案;反之亦然(方案测评标准)
命名空间是一种绝妙的理念,我们应当多加利用(倡导与号召)

如何写简洁的代码

知道什么是简洁的代码不代表能写出简洁的代码。就像可以用数学公式来完全描绘出来骑自行车的过程,但不代表就会骑车了。这一点需要持之以恒的练习。一些通用的方法包括:

  • 好的命名
  • 合适的抽象(数据、过程)
  • 不多不少的注释
  • 统一的格式

一些详细的方法在《代码整洁之道》里都有详细的介绍。

在写代码的时候,不要只是为了能够实现这个需求,而要考虑实现的逻辑合不合理。一种不好的实现是:改了几遍,它最后能工作了,但我明天就不清楚它为啥能工作了

读的话,如果能读各种源码的话最好,另一个很有用的方法就是读自己的代码。每隔一段时间读一下自己之前写的代码,总是能发现可以提高的地方。

一些值得参考的代码规范

需要有强制的代码风格吗?

我们有一个统一推荐的代码风格,但是由于历史原因,我们并没有强制执行。因为这些代码写出来的时候还没有这一规定,我们的首选是兼容它的风格(在修改其他人代码的时候也遵循这一准则);

另外也有意见认为,代码风格是相对虚幻的东西,强制团队内所有人风格一致会产生更多问题。

所以,我们有统一推荐的风格,期望大家能尽量保持统一;也允许有一些个人的风格。但是有一点是确认的:不管是自己新建,还是修改别人的代码,同一个模块、文件、函数内的风格要保持一致。如果自己新建文件,就自己来维护风格一致;如果修改别人的文件,就与原文件保持兼容风格。所以我们也提供了统一的格式化工具,鼓励大家在可能的情况下来使用它来保持整个项目风格统一。

我们采用的一些风格与其原因:

格式化工具 prettier 有其默认风格。除了默认风格外,我们有一些自定义的。

抛开格式化工具,一些值得注意的风格是:

printWidth: 100:单行最大字符数

默认是 80,由于现在显示器越来越大,所以我们扩展到 100,但是我们经常还会看到超过 150,甚至 200 的,这种不换号的话一个显示器都显示不下了。

// VSCode 添加标尺,提示自己的代码已经过长了
"editor.rulers": [
    80,
    100,
],

trailingComma: 'all': 多行模式添加逗号

这个主要是为了减少 git diff。

// before
let items = [
    1,
    2,
    3
]

// after
let items = [
    1,
    2,
    3,
    4
]

上述例子,在向数组添加一个元素后,会引起一行修改(3)和一行添加(4)。

let items = [
    1,
    2,
-   3
+   3,
+   4
]

如果 3 的后边加了逗号,就会只有一行添加。

let items = [
    1,
    2,
    3,
+   4,
]

二元操作符中间加空格

let x=a+b-c+d

vs

let x = a + b - c + d

尽量少用三元操作符

let result = value === '1' ? '一' : value === '2' ? '二' : '三'

// vs

let result
switch (value) {
    case '1':
        result = '一'
        break
    case '2':
        result = '二'
        break
    default:
        result = '三'
        break
}

// 或者
let result = {
    '1': '一',
    '2': '二',
}[value] || '三'

采用 4 个空格缩进

这是一个约定俗成的,但是也可以用 2 个,或者 Tab。唯一要确定的是:不能有混用的情况。

类似的还有很多,可以去 Airbnb 风格推荐那里查看。

一个例子

下边是一个简单的例子,但是暴露的问题在我们的代码中都是真实存在的(第一眼,你能找到这个组件的 render 方法吗?)。

  • import 与 class 之间应该有空行
  • 混乱的缩进,不统一而且混合用了 Tab 和空格
  • <Button> 元素这一行太长
  • 单双引号混用
  • 混乱的命名:同一个函数内 item vs itmind vs idx
  • 类名没有遵循规范(App not app)
  • 各种操作符之间缺少空格,可读性很差
  • 无用的引入:InputModal
  • 错误的使用数组方法(map 返回了一个无用的数组)

我想读这样的代码肯定很折磨,更不用说要在这样的代码基础上做修改,估计很快头就大了。

import React,{Component} from 'react';

import {Button,message,Input,  Modal} from "antd"
class app extends Component{
        items = [{
                items:[1,2,3]},{
                    items:[4,5,6]
                }
        ]
onClick=()=>{
                try{
            message.success('ok')
        }

 catch(error) {
            message.error('not ok')
           }


      finally {
            message.success('finally')
        }
    this.items.map((item, ind)=>{
        item.items.forEach((itm, idx)=>{
            // doSth
        })
    })
    }
      render() {
    return <Button onClick = {this.onClick} type="primary" size="small" shape="circle" htmlType="submit" icon="submit" ghost disabled loading>按钮</Button>
}
}
export default app

但是我们只需要做出一点改变,就能很大的提升代码可读性。上边列出的大部分问题都可以用格式化工具来解决,命名方面的,只需要我们多用点心,就可以提升可辨识度。下边是修改后的代码:

import React, { Component } from 'react'
import { Button, message } from 'antd'

class App extends Component {
    items = [
        {
            items: [1, 2, 3],
        },
        {
            items: [4, 5, 6],
        },
    ]

    onClick = () => {
        try {
            message.success('ok')
        } catch (error) {
            message.error('not ok')
        } finally {
            message.success('finally')
        }
        this.items.forEach((item, index) => {
            item.items.forEach((subItem, subIndex) => {
                // doSth
            })
        })
    }

    render() {
        return (
            <Button
                onClick={this.onClick}
                type="primary"
                size="small"
                shape="circle"
                htmlType="submit"
                icon="submit"
                ghost
                disabled
                loading
            >
                按钮
            </Button>
        )
    }
}
export default App

PS

有无数的指南教我们如何写简洁的代码,但是即便是有经验的程序员也不能写出百分之百简洁优美的代码,Dan Abramov,React 核心开发者,Redux 作者,在前几天发推说

I used to think “clean” means code broken down in small functions, no repetition, no comments. Now I think of it more as code with few possible control flow combinations, direct style (can always trace what connects to what), doesn’t violate grep test, comments explain why.

我曾经认为“整洁”意味着代码被分解为小的函数,没有重复,没有注释。 现在我更倾向于认为是尽可能少地使用控制流组合,风格直接(总是可以追踪什么是什么),不违反grep测试,注释解释原因。

只有不停地练习并思考,才能持续地改进。



发表评论




0条评论