代码书写规范总纲

JeremyJone ... 2023-7-13 大约 6 分钟

# 代码书写规范总纲

作者:JeremyJone

版本:v1

版权:Jz


良好的规范,促进高质量的代码。 --与君共勉

# 前言

本《规范》旨在帮助编写更加清晰工整的代码,它是一种认为的,作为约束那些不在编译错误范围内的不良习惯。

在一开始编写代码,会有很多不良习惯,有的是为了方便图省事,有的是确实不知道有什么更好的方式方法,时间久了,造成了一种不良的编写习惯,不仅再次翻阅时头痛,也会增加维护成本,有甚者会失去一些工作机会等。

为了避免和规范代码,特制定本《规范》,它适用于大多数情况,如有特例,必要时应使用注释等方式进行显式注明其作用。

# 通用规则

有一些规范是不依赖任何语言的,当然有些语言对这些规则也有一些独特的规范化提案,我们应当在遵循这些提案的情况下,适配本《规范》,它的优先级更低。

# 使用多文件

在项目中,应当使用多个文件和文件夹,以区分每一个文件的功能,而需要将功能类似的文件放在同一个文件夹中,进一步加以区分项目的整体结构。

  • 不同功能应当区分

  • 创建的每一个类都应当是单独的文件

  • 文件和文件夹的名称应当具有显式的意义

这个在不同语言中的要求不尽相同,但是更多的是在设计阶段,就应该为整个项目设计好整体的目录结构,分成不同模块,分工协作开发。

# 使用缩进

不同的代码块作用域,应当使用不同量的缩进予以对应,这样看上去具有更加清晰的层级,方便编写和检查。

  • 每一级缩进应当使用 4 个 space,而不是一个 tab

  • 在同一个项目中,应当使用相同 space 的缩进量

  • 作用域的大括号(如果有),只要括号不与代码同行,都应当与代码左对齐

  • 在书写大括号时,应当每次直接书写完整的括号,然后在其内部编写代码

一个不好的例子:

#include <stdio.h>
int main() {
int i, j;
for(i=0;i<10;i++) {
for(j=0;j<10;j++) {
printf("%d - %d", i, j);
}
}
return 0;
}
1
2
3
4
5
6
7
8
9
10

一个好的例子:

#include <stdio.h>
int main() {
    int i, j;

    for (i = 0; i < 10; i++) {
        for (j = 0; j < 10; j++) {
            printf("%d - %d", i, j);
        }
    }

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12

不难看出,前者很难看出区别,而后者结构清晰,可以一眼看出结构格式甚至逻辑。

# 使用空行

空行起到分隔段落的作用,使得整个程序结构更加清晰。

  • 定义变量后需要空一行

  • 每个函数定义结束后需要空至少一行

  • 类之间需要空至少两行(原则上每个类都应该不同文件)

  • 在同一个作用域中,不同的逻辑内容需要使用空行分开,加以区分其功能的不同性。

示例:



 



 



int main() {
    int i;

    for (i = 0; i < 10; i++) {
        // ...
    }

    return 0;
}
1
2
3
4
5
6
7
8
9

# 使用空格

空格可以使同行内的代码结构变得更加清晰明了。

  • 行尾不要有空格

  • 函数名之后不要有空格

  • 一般情况下,不要在同一行出现多个表达式,否则使用一个空格分开

  • 关键字之后需要有一个空格。比如 ifforwhile

  • 赋值运算符、关系运算符、算术运算符、逻辑运算符、位运算符等都需要左右使用一个空格

  • 单目运算符等前后不加空格

示例:

 



for (i = 0; i < 10; i++) {
    // ...
}
1
2
3

# 有效的名称

在代码中,所有变量、函数、类都具有一个名称,它方便了程序员编写代码。很多时候,一个有效的名称可以减少大量的不必要工作和时间。

一个简单的例子:

int a = 18;
int age = 18;
1
2

很明显,使用第二行的 age 可以更加清晰的表述这个数字的具体含义,这为之后的引用或修改提供了帮助。

# 变量名

每一个变量名都应当具有其显式的意义,任何违背此原则的变量名,都属于一个不好的名称。

  • 使用具名含义的名词,如 age、size、width 等

  • 使用特殊含义的词语,来限定其特殊意义,如 is、can、has 等

    • 用来衡量 是不是 的含义的,使用 is 开头 + 本体词语

    • 用来衡量 行不行 的含义的,使用 can 开头 + 本体词语

    • 用来衡量 有没有 的含义的,使用 has 开头 + 本体词语

    • C++的成员变量通常需要加上 m 前缀,表示其为成员变量

  • 变量通常使用 小驼峰格式 或 全小写 + 下划线 _ 的格式

  • 常量应当使用全大写 + 下划线 _ 的格式

同时,一些含义模糊的名称应当避免使用。比如:

// wrong
int number = 1; // 这是什么数字?
bool flag = true; // 这是哪个标志位?

// right
int resNum = 1; // 结果的数字,result 简写为 res 是可以的,它是通用的
bool okFlag = true; // 成功的标志位,意味着成功会是 ok 的,不需要再去翻阅上下文查看 flag 的含义
1
2
3
4
5
6
7

# 函数名

函数名称应当显式的给出其作用、返回值等信息,不应当使用无意义的 fn 等。

通常,函数名应当使用一个 动词 + 名词 的结构格式来命名。

常用的函数前缀包括:

前缀 功能
get 获取某个值
set 赋值
handle 操作功能

例如:

public String getName() {
    return name;
}

public void setName(String n) {
    name = n;
}
1
2
3
4
5
6
7

# 有用的注释

注释通常是给程序员使用,方便记录下当前写下此处代码的逻辑想法,为了以后再次查看或修改时使用。

当然,在逻辑结构中,99%的逻辑代码都可以使用 有用的变量名称 替代。

例如:

// wrong
function getWidth() {
    // 默认宽度的最小值
    const size = 10;

    // 这里需要判断是否小于最小值,如果小于,返回最小值
    if (this.width < size) {
        return size;
    }

    return this.width;
}

// right
function getWidth() {
    const defaultMinWidth = 10;

    if (this.width < defaultMinWidth) {
        return defaultMinWidth;
    }

    return this.width;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

上例中,明显下面的正确方式不再需要任何注释,并且可以确保每一行都能看得明白。

所以,上面的错误示例中,第3行和第6行的注释,就属于无用的注释,它们完全可以被更加精良的代码所替代。

# 具体的语言

每种语言都有自己独特的使用方式。所以《规范》应当针对不同语言具有不同的内容。请记住,任何情况下,具体语言的规范优先级都应该高于通用规范。