[Clean Code] Chapter 5: 格式化你的代码!

Chapter 5: Formatting


1-为什么要格式化我们的代码?

如果我们的代码遵循着某种规则,干净利落,那么我们的想法可以顺利自然的表达出来。
代码的格式化之于可读性如同说话的技巧之于沟通交流啊。


纵向格式


2-源文件大小-行数

作者统计了各种开源软件的代码,做出结论:基本上200行,不超过500行。


3-空行间隔

适当的空行间隔可以美化代码,使其看起来更舒服。(然而 空行也不要太多,一般不要连续空行)
比较以下代码

// 代码1
package fitnesse.wikitext.widgets;

import java.util.regex.*;

public class BoldWidget extends ParentWidget {
    public static final String REGEXP = "'''.+?'''";
    private static final Pattern pattern = Pattern.compile("'''(.+?)'''",
        Pattern.MULTILINE + Pattern.DOTALL
    );

    public BoldWidget(ParentWidget parent, String text) throws Exception {  
        super(parent);
        Matcher match = pattern.matcher(text);
        match.find();
        addChildWidgets(match.group(1));
    }

    public String render() throws Exception {
        StringBuffer html = new StringBuffer("<b>"); html.append(childHtml()).append("</b>");
        return html.toString();
    }
}
// 代码2
package fitnesse.wikitext.widgets;
import java.util.regex.*;
public class BoldWidget extends ParentWidget {
    public static final String REGEXP = "'''.+?'''";
    private static final Pattern pattern = Pattern.compile("'''(.+?)'''",
        Pattern.MULTILINE + Pattern.DOTALL );
    public BoldWidget(ParentWidget parent, String text) throws Exception {  
        super(parent);
        Matcher match = pattern.matcher(text);
        match.find();
        addChildWidgets(match.group(1));
    }
    public String render() throws Exception {
        StringBuffer html = new StringBuffer("<b>"); html.append(childHtml()).append("</b>");
        return html.toString();
    }
}

4-行的密度

我们应当将联系紧密的代码上下紧密连接,使之更容易阅读。
顺便吐槽一下,那些没用的注释,删掉他们可以使得代码更紧凑,更好看。

  • 变量声明: 变量应该尽可能的靠近用它的那段代码。 当我们的函数比较短的时候,我们会将变量声明放在函数的开始。

    • 实例变量(instance variable):实例变量定义在对象一级,它可以被类中的任何方法或者其他类中的方法访问,但是不能被静态方法访问。
      • java: 全部放在顶部
      • c++: 放在底部
  • 有依赖关系的函数
    如果一个函数调用了其他的函数,那么他们应该放在一起,而且被调用者放在调用者上面

  • 概念上类似的函数放在一起

// 代码3
// 这些在概念上都是一个类型的代码,我们应该将他们放在一起
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, !condition);
    }

    static public void assertFalse(boolean condition) {
        assertFalse(null, condition);
    }
    ...

另外,在java经常要写get/set函数,把所有的get/set都放在一起也是显而易见的。


5-整体顺序布局

  • 函数:被调用者放在调用者上面。
  • 最重要的概念放在最上面。

横向格式


6-横向长度

- 永远不要写到 需要滚动屏幕 才能看到的长度!
- 字符数 < 120 //因为你可以调小字体大小使其满足1= =

7-横向空格间隙

看如下代码

// 代码4, 关注函数声明部分和+-等操作符部分
public class Quadratic {
    public static double root1(double a, double b, double c) {
        double determinant = determinant(a, b, c);
        return (-b + Math.sqrt(determinant)) / (2*a);
    }

    public static double root2(int a, int b, int c) {
        double determinant = determinant(a, b, c);
        return (-b - Math.sqrt(determinant)) / (2*a);
    }

    private static double determinant(double a, double b, double c) {
        return b*b - 4*a*c;
    }
}

8-对齐

假如代码4这么写

// 代码4-2, 理解难度+max
public class Quadratic {
public static double root1(double a, double b, double c) {
double determinant = determinant(a, b, c);
return (-b + Math.sqrt(determinant)) / (2*a);}

public static double root2(int a, int b, int c) {
double determinant = determinant(a, b, c);
return (-b - Math.sqrt(determinant)) / (2*a);}

private static double determinant(double a, double b, double c) {
return b*b - 4*a*c;}
}

一段对齐的代码一眼看去就能感觉其中的结构,使得我们更容易的理解代码的意思。

实际上,在我们平时写代码的时候,因为函数,while等语句很短,我们会写作一行。
作者认为不要这样。他认为坚持对齐格式是更好的选择。
我自己的代码(反例):

// 代码5
// 快排的一个函数,选定pivot,然后交换
int partition(int arr_int[], int start, int end) {
    int pivot_value = arr_int[end];
    int left_index = start;
    int right_index = end - 1;

    while (true) {
        // 以下两个while 和一个if 都写作了一行
        while (arr_int[left_index] <= pivot_value && left_index < end) ++left_index;
        while (arr_int[right_index] >= pivot_value && right_index > start) --right_index;

        if (left_index >= right_index) break;

        swap_int(arr_int[left_index], arr_int[right_index]);
    }

    swap_int(arr_int[left_index], arr_int[end]);

    return left_index;
}

更极端的情况,我们可能会写成这样

// 代码6
while (arr_int[left_index++] <= pivot_value && left_index < end);
while (arr_int[right_index++] >= pivot_value && right_index > start);

作者认为这样是非常不可取的,因为其他人在读代码的时候很可能就犯蒙bile。因为万一读代码的人没看到这个分号,你懂的。。。


9-开发同个项目的整个团队应有相同的代码风格


10-作者自己代码风格(java)

作者给出了自己的代码。
从中看到:

  • 函数之间空行1
  • 函数内部不空行
  • while if 不单行
  • 只有一句话的if while 省去了 {}
  • 数学操作符只有明显分块的时候会空开,一般不空
// 代码7
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())
                findJavaFiles(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 = getSortedWidths();
        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]));
        Arrays.sort(sortedWidths);
        return sortedWidths;
    }
}

代码5 按照作者的风格可以改成如下排版

// 代码8
int partition(int arr_int[], int start, int end) {
    int pivot_value = arr_int[end];
    int left_index = start;
    int right_index = end - 1;
    while (true) {
        while (arr_int[left_index] <= pivot_value && left_index < end)  
            ++left_index;
        while (arr_int[right_index] >= pivot_value && right_index > start)
            --right_index;
        if (left_index >= right_index)
            break;
        swap_int(arr_int[left_index], arr_int[right_index]);
    }
    swap_int(arr_int[left_index], arr_int[end]);
    return left_index;
}

总结

选择一种流行的format,在你所有的代码中都遵守它!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值