选用一套管理代码格式的简单规则,然后贯彻这些规则
其实现在的 IDE 基本上都有非常标准的代码格式化规则,我们只需要在编写代码的过程中时常格式化代码即可
5.1 格式的目的
- 代码格式很重要
- 代码格式不可忽略,必须严肃对待
- 代码的可读性会对以后可能产生的修改行为产生深远影响
5.2 垂直格式
- 每个文件的代码行数不要过多,尽量保持在 100 行以内,尽量不要超过 200 行
- 短文件通常比长文件易于理解
5.2.1 向报纸学习
- 名称应当简单且一目了然
- 源文件最顶部应该给出高层次概念和算法
- 细节应该往下渐次展开,直至找到源文件最底层的函数和细节
5.2.2 概念间垂直方向上的区隔
- 每行展现一个表达式或一个子句,每组代码展示一条完整的思路
- 这些思路之间应该用空白行区隔开
- 每个空白行都是一条线索,标识出新的独立概念
5.2.3 垂直方向上的靠近
- 紧密相关的代码应该互相靠近
5.2.4 垂直距离
- 对于那些关系密切,放置于同一源文件中的概念,它们之间的区隔应该成为对相互易懂程度有多重要的衡量标准
- 应该避免迫使读者在源文件和类中跳来跳去
- 变量声明应该尽可能的靠近其使用位置,仅限于局部变量
- 循环中的控制变量应该总是在循环语句中声明
- 实体变量应该在类的顶部声明,就是指的全局变量
- 若某个函数调用了另外一个,就应该把它们放到一起,而且调用者应该尽可能的放在被调用者上面
- 这一点和我平时的习惯有冲突,我一般喜欢把私有的函数从文件底部开始编写,公开的函数从文件顶部开始编写
- 概念相关的代码应该放到一起,相关性越强,彼此之间的距离就该越短
Good Example
- 这是 Junit4 的代码片段,这段代码遵循了作者上述描述中的所有规则
- 我一般习惯把被调用的函数放在下面,同时喜欢把私有函数和共有函数区分开
public class Assert {
static public void assertTrue(String message, boolean condition) {
if (!condition) {
fail(message);
}
}
static public void assertTrue(boolean condition) {
assertTrue(null, condition);
}
static public void assertFalse(String message, boolean condition) {
assertTrue(message, !condtion);
}
static public void assertFalse(boolean condition) {
assertFalse(null, condtion);
}
}
5.2.5 垂直顺序
- 被调用的函数应该放在执行调用的函数下面
- 最重要的概念先出来,通过最少细节表述它们,底层细节最后出来
5.3 横向格式
- 代码长度达到 100 个字符或者 120 个字符是作者认为已经比较长了
- 但作者也说到,现在显示屏越来越大,分辨率越来越高,所以像 100 - 120 个字符的长度也可以接受,但真的不建议更长
5.3.1 水平方向上的区隔与靠近
- 我们使用空格字符将彼此紧密相关的事物连接到一起
- 也用空格字符把相关性较弱的事物分隔开
- 这些规则现在的 IDE 都会通过格式化代码做到准确无误
- 我个人比较喜欢单行注释后面加一个空格在编写内容,例如
// 这里是注释
,而不是//这里是注释
5.3.2 水平对齐
- 老旧的习惯,没什么用
5.3.3 缩紧
- 遵循 IDE 规则即可
- 唯一需要注意的是缩紧的实际空格大小,例如 Java 中的一个 Tab 缩紧表示的是 4 个空格,而其他的例如 JS 就是两个空格
- 我习惯于除了 Java 以外,其他的语言都使用两个空格作为一个 Tab 的缩紧
5.3.4 空范围
- 尽量不要使用将 while 或 for 语句的语句体置为空的写法
5.4 团队规则
- 一组开发者应当认同一种格式风格,每个成员都应该采用那种风格
- 好的软件系统是由一系列读起来不错的代码文件组成,它们需要拥有一致和顺畅的风格
- 绝对不要用各种不同的风格来编写源代码,这样会增加其复杂度
5.5 鲍勃大叔的格式规则
- 作者认为下面这段代码是他写的可以作为 最好的编码便准文档的范例
- 我觉得大部分还比较认可,几个小细节跟我习惯性的有点出入
public class CodeAnalyzer implements JavaFileAnalysis {
private int lineCount;
private int maxLineWidth;
private int widestLineNumber;
private LineWidthHistogram lineWidthHistogram;
private int totalChars;
public CodeAnalyzer() {
lineWidthHistogram = new LineWidthHistogram;
}
public static List<File> findJavaFiles(File parentDirectory) {
List<File> files = new ArrayList<File>();
findJavaFiles(parentDirectory, files);
return files;
}
private static void findJavaFiles(File parentDirectory, List<File> files) {
for (File file : parentDirectory.listFiles()) {
if (file.getName().endsWith(“.java”)) {
files.add(file);
} else if (file.isDirectory()) {
findJavaFile(file, files);
}
}
}
public void analyzeFile(File javaFile) throws Exception {
BufferedReader br = new BufferedReader(new FileReader(javaFile));
String line;
while ((line = br.readLine()) != null) {
measureLine(line);
}
}
private void measureLine(String line) {
lineCount++;
int lineSize = line.length();
totalChars += lineSize;
lineWidthHistogram.addLine(lineSize, lineCount);
recordWidestLine(lineSize);
}
private void recordWidestLine(int lineSize) {
if (lineSize > maxLineWidth) {
maxLineWidth = lineSize;
widestLineNumber = lineCount;
}
}
public int getLineCount() {
return lineCount;
}
public int getMaxLineWidth() {
return maxLineWidth;
}
public int getWidestLineNumber() {
return widestLineNumber;
}
public LineWidthHistogram getLineWidthHistogram() {
return lineWidthHistogram;
}
public double getMeanLineWidth() {
return (double) totalChars / lineCount;
}
public int getMedianLineWidth() {
Integer[] sortedWidths = getSortedWidth();
int cumulativeLineCount = 0;
for (int width : sortedWidths) {
cumulativeLineCount += lineCountForWidth(width);
if (cumulativeLineCount > lineCount / 2) {
return width;
}
}
throw new Error(“Cannot get here”);
}
private int lineCountForWidth(int width) {
return lineWidthHistogram.getLinesForWidth(width).size();
}
private Integer[] getSortedWidths() {
Set<Integer> widths = lineWidthHistogram.getWidths();
Integer[] sortedWidths = (widths.toArray(new Integer[0]));
Array.sort(sortedWidths);
return sortedWidths;
}
}