選用一套管理代碼格式的簡單規則,然後貫徹這些規則
其實現在的 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;
}
}