zoukankan      html  css  js  c++  java
  • 《编写可读代码的艺术》---该写什么样的注释

    本章旨在帮助各位读者们去了解应该写什么样子的注释,你可能以为注释的目的就是解释代码做了什么,没错,但这只是其中一部分。

    注释的目的

    尽量传递信息给读者,使其对代码的熟悉程度和作者一致。

    当你写代码的时候,脑海里有很多有价值的信息,你没有选择记录下来;

    当其他人阅读你的代码的时候,这些信息就丢失了---它们看到的只有代码。

     因此,本章主要围绕以下三点展开:

    1. 仅当需要的时候,才使用注释
    2. 记下有价值的信息
    3. 站在读者的角度,去想象他们需要什么

    仅当需要的时候,才使用注释

    1  如果代码本身可以快速推断出其意义,就没必要写注释。

    注释会分散读者理解代码的注意力,会占用屏幕空间,因此,注释是要有存在价值的。

    比如下面的代码里面的注释对于读者而言,是没有价值的。

     /// <summary>
        /// 日期工具类,提供日期的一系列函数
        /// </summary>
        class DateHelper
        {
            /// <summary>
            /// 私有日期变量
            /// </summary>
            private DateTime _currenDateTime = DateTime.Now;
    
            /// <summary>
            /// 默认构造器
            /// </summary>
            public DateHelper()
            {
                
            }
    
            /// <summary>
            /// 获取日期
            /// </summary>
            /// <returns></returns>
            public DateTime GetTime()
            {
                return _currenDateTime;
            }
            /// <summary>
            /// 设置日期
            /// </summary>
            /// <param name="newdate"></param>
            public void SetTime(DateTime newdate)
            {
                _currenDateTime = newdate;
            }
        }

    2 注释不是复述一遍代码

    如果注释仅仅只是复述了一遍代码字面上的意思,那么它对读者的价值就大大降低,这种情况,要么删掉注释;要么让注释传递更多的信息

    我们来看个例子:

              //使用指定名称在树指定的深度查找节点
              Node FindNodeInTheTree(Tree tree, string nodeName, int searchDepth);

    很显然,这个注释就属于“复述型”,要么删除它

    如果查找方法还有其他细节的,你可以选择完善注释,比如下面的修改方案

              //通过名称查找指定的节点,否则返回null
              //如果searchDepth<=0,那么只在tree根目录查找
              Node FindNodeInTheTree(Tree tree, string nodeName, int searchDepth);

    3  注释不应该拿来粉饰

    注释不应该拿来粉饰不友好的命名,应该使用重构工具来重命名一个友好的名字。

    比如,我们有个方法,在公司人员离职的时候调用

            // 当员工离职的时候,调用本方法
            // 这个方法不会从数据库物理删除该用户所有数据
            // 只是将用户的状态设置为禁用
            bool DeleteUser(Guid userGuid);

    这个时候,我们与其拿注释来粉饰这个糟糕的方法名称,还不如重构它

            bool DisableUserWhenDimission(Guid userGuid);

    好的代码 》 坏的代码+好的注释

    记下有价值的信息

    1 加入你的心得

    1.1 探过的路,不必再探

    比如:有个方法需要用到排序。

            //在测试中,我们比较了快速排序、选择排序、冒泡排序
            //最终我们选择快速排序,因为对比发现,快速排序速度最快,占用内存最小

    1.2 踩过的坑,不要再踩

    比如我们系统中,引入了第三方插件,但是有个BUG

            //这个插件在随机创建用户名的时候
            //极小情况下,会出现用户名重复的情况
            //已经反馈给作者,等待作者修改,不是系统的BUG

    2 为代码中的瑕疵注释

    在敏捷开发里面,更新非常频繁,代码在不断的成长,最终演变成最终版。但是在这个迭代的过程中,代码肯定会存在瑕疵,我们就可以通过注释,将这些瑕疵记录下来。

    这种注释给读者带来对代码质量和当前状态的宝贵理解,甚至可能会给他们指出如何改进代码的方向。

            //TODO   我还没有处理完的事情
            //FIXME   已知的有异常的代码
            //HACK    有待优化的低劣方式
            //XXX      危险! 这里有问题

    3 给常量加注释

    简单点讲,就是回答读者:为啥这个常量取这个值。

    3.1 常量调整指南

             //线程的数量:经过测试,发现如果该值<= 2*cpu核的时候,系统表现良好
             const int THREAD_COUNT = 6;

    3.2 不要乱动

            //人类的移动速度:经过多次调整,发现在3.14速度下,人类的移动速度最自然
            private const float PEOPLE_MOVE_SPEED = 3.14f;

    3.3 补充说明

            //人类体重的限制,额,正常人不会超过这个重量吧
            const int MAX_HUMAN_WEIGHT = 500;

    有些常量如果本身名称就足够清晰,那么就不需要注释,比如(PI=3.1415;MINUTES_PER_DAY=1440)

    4 站在读者的角度

    这个“读者”可以是不熟悉项目的人,也可以使若干个月之后我们。想象下,我们平时写的代码,对于刚接手的外人来讲,是什么样子?是泥淖还是风景?

    4.1 意料之中的提问

    很多网站上都专门开辟了常见问题(F&Q)专栏,对读者80%的常见问题,进行统一回答,省时省力。

    因此,你觉得作者在读我们的代码的时候,遇到某些模块,会有“什么?为什么要写成这样?”的问题的时候,就可以给这个问题加上注释。

    比如下面的c++代码,我们要清空一个数组,读者看到Clear方法可能会疑惑

    Struct Recoder{
    vector<float> data;
    //....
    
    void Clear(){
    //清空数组为啥不用data.clear()?
    vector<float>().swap(data);
    }
    
    }

    所以我们要做的是回答读者,为啥不使用data.clear()这个常用清空数组的方法?

    //实际上,只有这样才会强制让data真正的把内存归还给内存分配器
    //详情参阅:“STL swap trick”
    vector<float>().swap(data);

    4.2 公布缺陷

    当为一个函数或者类的写文档的时候,可以问自己“这段代码有啥出乎意料的地方,会不会被误用?”。

    也就是说,你要“未雨绸缪”,预料到别人使用你代码的时候,遇到的问题。

    比如一个向用户手机发送验证码的函数:

    void SendMessageToPhone(string to,string subject,string body);

    但是因为短信提供商的问题,这个方法有可能会延迟几个小时(不是实时的),

    所以,我们有必要给这个函数打上注释

    //给用户手机发送短信,因为运营商的关系,可能会造成延时,(我们试过1个多小时后才收到短信。。擦)
    void SendMessageToPhone(string to,string subject,string body);

    4.3 全局观注释

    对于团队的新成员来讲,最难以理解的事情之一就是理解“全局观”---类之间如何交互啊、数据如何在整个系统中流动啊、项目入口点在哪里啊。。。balabalabala

    设计系统的人常常忘记给这些点加上注释,因为他们觉得这些很容易理解,以至于不需要注释。

    想象下这样的场景:“团队来了个新人,他坐在你旁边,而你的任务是让他快速熟悉代码,进入到开发的角色中。”

    因此,你可能会带着他遨游现有的代码库,指着某些文件、代码侃侃而谈:

    “这段代码把我们的业务逻辑和数据库黏在一起,任何应用层的代码都不应该和他交互。”

    “这个类看上去很复杂,实际上,它只是一个巧妙的缓存,它对系统中其他部分一无所知。”

    “页面的事件执行顺序是a->b->c->d,我们之所以要这样设计,是因为balabalaba。”

    “这个类包含一些辅助的函数,为我们的网站后台管理提供了便利的接口,用来处理用户的权限问题,比如登陆、管理”

    相比阅读源码这样的交谈是不是让新人获益良多?那为什么我们不把他们整理下,写到注释中呢?  “好记性不如烂笔头”

    4.4 总结性注释

    方法内部如果逻辑比较多,或者比较复杂。除了第一步进行重构,第二步我们可以给对应的代码块写上注释

    对这个函数所做的事情做个总结,因此读者在深入了解代码前,对这个代码干啥用的已经了然于胸。

     void ShowCustomBoughtItems(string userName)
            {
              //1 根据用户名从数据库中查找出该用户ID
    
              //2 根据用户ID从商品-用户关联表里找出商品ID列表
    
              //3 根据商品ID关联商品表,获取对应的商品详情
    
              //4 列出这些详情
            }

    你可以做任何帮助读者更理解代码的事情,而不必纠结于注释的职责。“做什么”、“怎么做”、“为什么这样做”都可以拿来活用。

  • 相关阅读:
    python简介
    计算机基础
    C# 验证数字
    在字符串指定的索引下添加字符,输出换行
    js中实现子页面向父页面中赋值
    js搜索相同类型的控件全选、取值(Checkbox)
    Nhibernate中多Or条件的查询,很多Or的查询
    js遍历checkbox获取数据
    Jquery获取web窗体关闭事件,排除刷新页面
    两年多的工作感悟
  • 原文地址:https://www.cnblogs.com/kimmy/p/3671235.html
Copyright © 2011-2022 走看看