美文网首页IT干货程序员PMbook
读《编程匠艺—编写卓越的代码》:编写代码注释

读《编程匠艺—编写卓越的代码》:编写代码注释

作者: 思学 | 来源:发表于2015-01-26 21:58 被阅读337次

    查尔斯.普雷斯特.斯科特(Charles Prestwich Scott):
    评论是自由的,但事实是神圣的

    注释可以将优秀的代码和糟糕的代码区分开来,将艰涩难懂的逻辑与清晰友好的算法区分开。但我们也无须过分夸大注释的作用。如果说你的代码是蛋糕,那么你的注释就如同蛋糕上糖衣,它被精巧地涂抹以增加蛋糕的美感和价值,但不能掩饰蛋糕的裂缝和瑕疵。

    一、什么是代码注释

    • 从语法角度看,注释是编译器将忽略不计的源代码。
    • 从语义角度看,注释是对其所处位置的代码的注解。
      注释的作用有多种:
      1) 强调某个特殊的问题领域;
      2)在头文件中记录媒介;
      3)描述某个算法,协助维护程序;
      4)分割各个函数以帮组快速浏览整个源文件。
    • 注释是内部的文档化机制

    二、注释标记

    • C语言的注/*开头,*/结束,可以跨多行。
    • C++、C99、C#和Java语言增加了以//作为开头的单行注释。
    • 其他语言也提供了类似的块注释和行注释功能,语法可能不同。

    三、多少注释是恰当的

    小威廉.斯特伦克(William Strunk Jr.):
    铿锵有力之文必简洁

    • 把重点放在注释的质量上,而不是数量上;比编写多少注释更重要的是注释的内容。
    • 只在能为代码增加色彩时才编写注释。优秀的演奏家追求用最少的演奏创造出最美的声音
    • 真正好的代码并不需要注释,它们能自我解释。像f()g()这样的函数名才会大声嚷嚷,要求注释说明它们,但是像someGoodExample()这样的函数名根本不需要注释。

    四、注释中应该有什么

    贺瑞斯(Horace):
    写好源头和本源是明智的做法

    不好的注释比没有注释更糟糕,它们歪曲事实进而误导读者。

    1. 解释为什么,而不是怎么样
      (a).注释不是用来描述程序是怎样运行的,代码自己是其最权威的描述。
      (b).注释应该描述代码块起什么作用
      /* update WidgetList structure from GibWLRegistry*/这样的注释,不如写/* cache widget information for later*/。注释同一段代码,后者表达代码目的,而前者只告诉你这段代码做了什么。
      (c).注释可用来解释为什么代码要这么写。如有两种可供选择的实现方式,你决定采用其中一种,也许你该使用注释来解释一下,为什么你选择了这种实现方式。

    2. 不要取代或重复代码
      (a).如果注释描述的内容可以由编程语言本身实现,那么就试着用具体语句表达它。
      (b).如果你发现你用大量注释解释某个算法如何运行,请赶快停止,然后考虑是否需要重构代码或算法。
      (c).不要用注释描述变量的用法,重命名变量。
      (d).如果你要文档化一个应当总是成立的条件,也许你该写一个断言

    3. 确保注释有用
      注释的作用是标注和解释代码。注释需要满足:
      (a).记录意想不到的内容。如果代码有一部分是不常见、意想不到的,用注释记录下来;如果有特定问题需要规避,如一个操作系统问题,应该在注释中说明。
      (b).讲真话。不要写不准确的注释。
      (c).清晰明了。不要让注释模棱两可,内容尽量具体;不一定是完整的、语法正确的句子,但必须是可读的;避免缩写。
      (d).只关注当前代码,避免分心。不要记录过去我们怎么做;不要把应当剔除的代码(如旧代码)包含在注释中;避免使用ASCII艺术;不要在代码块结尾添加多余的注释,如在if语句的后括号后注释//end if

    每当写完注释后,在代码上下文中回顾一遍这些注释,考虑一下:它们是否都是正确的信息,它们是否简洁,它们是否容易理解

    五、实践

    看看下面一小段C++代码:

    for (int i = 0; i < wlst.sz(); ++i)
    k(wlst[i])
    

    很明显,你根本搞不懂这是在干吗。应用合理排版规则和添加注释说明对它进行改进:

    // Iterate over all widgets in  the widget list
    for (int i = 0; i < wlst.sz(); ++i)
    {
            // print out this widget
            k(wlst[i]);
    }
    

    现在好多了,起码这段代码的目的清晰了。但它还是不太令人满足。使用恰当的函数名和变量重写代码:

    for (int i = 0; i < widgets.size(); ++i)
           printWidget(widgets[i]);
    

    再看代码,发现根本不需要注释,代码本身能自我描述了。

    这印证了:

    不要文档化差劲的代码——重写这些代码

    六、注释的一些细节及其作用

    根据你自己的品味,把下面的建议当作指导性的原则:

    • 一致性
      所有的注释应该清晰明了,前后一致。为你的注释选择一种特定的布局方式,始终坚持使用。

    • 清晰的块注释
      将首位标记(如C/C++中/**/)各自放在一行,凸显它们;让块注释左侧多缩进一个字符,让注释显的像一个整体。如

    /* 
       * This is 
       * a block
       * comment.
       */
    

    而不是这种

      /* 
    This is 
            a block
    comment.
      */
    
    • 缩进的注释
      注释不应该截断代码或打乱逻辑流程。让注释与对应的代码的缩进一样。
      看到下面这样的代码,无疑更费劲:
    void strangeCommentStyle()
    {
            for (int i = 0; i < JUST_ENOUGH; ++i)
            {
            //This is a meaning comment about the next line.
                      doSomethingMeaningful(i);
       // good comment 
                      doOtherOperation(i);
            }
    }     
    

    在不带括号的循环中,不要把注释放在单循环体语句之前,这会导致灾难性后果。

    • 行尾注释
      大多数注释都是放在各自的行上,但有时较短的单行注释可以跟在代码语句后头。这种情况下,在注释与代码之间留白是一种好习惯。例如:
    Class Point
    {
    public:
            ........
    private:
            int x;                 //X轴坐标值
            int y;                 //Y轴坐标值
            int z;                 //Z轴坐标值
    };
    

    如果行尾注释直接跟在声明后面,代码看起来会显得拥挤,读起来更费劲。

    • 选择一种维护成本较低的风格
      可以把更多时间放在编写代码而不是摆弄注释上。
    • 帮组阅读代码
      a).注释通常在它描述的代码上方,而不是下方。读者一般都是从上向下阅读,这样注释可以让读者为即将读到的内容做好准备。
      b).注释后面紧跟代码,代码后面放一个空白行,接下来是下一个注释代码段。这种代码与空白一起使用的方式,可以将代码分隔成"段落",益于阅读。
    • 分隔板
      注释经常被用作不同代码部分之间的“分隔板”。如在一个若干个类或函数的源文件中,可能有以下注释:
    /******************************************
     * class foo implementation
    ******************************************/
    

    ////////////////////////////////////////////////////
    

    我们应当避免大量使用这种"分隔板"类的注释。将代码分组的应该是良好的缩进和结构,而不是生动的ASCII艺术。

    • 标志
      注释还可以用作代码中内嵌的标志。如:
      a).//XXX用来标识棘手的代码或需要重新编写的代码;
      b).//FIXME表示已知已经损坏的代码;
      c).//TODO用来标识缺少一些功能,需要日后返工。

    • 文件头注释
      a).所有源文件都应该以描述其内容的注释块作为开头。这个头注释是一个概述前言,提供了文件的关键信息。
      b).头注释应该包含文件的目的、版本、所有权及版权声明;
      c).头注释不需要把文件中定义的所有函数、类、全局变量等内容罗列,太累赘。

    • 帮组编写程序
      a).一种常见的编码方式是首先用注释构造代码结构,然后在各行注释下面填写代码。这种方式要注意及时删除一些无用的注释和修改一些不恰当的注释。
      b).另一种常见的编码方式是徒手编写代码,再添加注释。可能常常忘记某项工作或不能写出好的注释。更好的方式是边写代码边写注释,实践会告诉你写多少注释。

    • 注释过时
      要明白注释会过时。当你修正、添加或修改任何代码时,请同时修正、添加或修改相应的注释。

    八、总结

    德尔莫.施瓦茨(Delmore Schwartz):
    重要的写作是要讲眼之所见,这样就不必一遍又一遍的口口相传了

    写高质量的注释比写高数量的注释更好,然而,最好的是写出高质量的代码——那些无需注释的自文档化代码。

    相关文章

      网友评论

        本文标题:读《编程匠艺—编写卓越的代码》:编写代码注释

        本文链接:https://www.haomeiwen.com/subject/afjfxttx.html