美文网首页
《编写可读代码的艺术》读书笔记(一)

《编写可读代码的艺术》读书笔记(一)

作者: 书呆子的复仇 | 来源:发表于2015-11-29 11:07 被阅读117次

    0. 前言

    程序员给人的印象大多都是蓬头垢面,衣着随意。然而,我认为一个优秀的程序员对于代码一定有他的审美。如果你做过很多项目,写过多年的代码,对程序还仅仅是停留在能够跑通,能实现某个功能就算完成了,对于代码的是否足够elegant没有任何的追求,我认为你不过就是一个code monkey。其实市面上类似的书有很多,例如《clean code》,这本书在深度上可能与《clean code》相比还有所不及,但在广度上,远远超过了《clean code》。因为它关注的是编码本身,所以并不局限于一种编程语言。作者对于代码的精雕细琢已经达到了一种令人发狂的地步,仿佛代码本身就是一件艺术品。如果你对于代码的腐烂有着一种敏锐的嗅觉,并且具有一种强迫症,想要努力去重构它,那么恭喜你,这本书适合你。最后我想引用软件设计大师Martin Fowler在他的《Refactoring : Improving the Design of Existing Code》中的一句话:

    任何一个傻瓜都能写出计算机可以理解的程序,只有写出人类容易理解的程序才是优秀的程序员。

    1.把信息装在名字里

    无论是名字变量、函数还是类,都可以使用很多相同的原则。我们喜欢把名字当作一个小小的注释。尽管空间不算很大,但选择一个好名字可以让它承载很多的信息。

    1.1选择专业的名词

    “把信息装在名字中”包括要选择非常专业的词,并且避免使用“空洞”的词,例如,“get”这个词就非常不专业,例如下面的例子:

    def getPage(url):...
    

    “get”这个词没有表达出很多信息。这个方法是从本次缓存中得到一个页面,还是从数据库中,或者从互联网中,更专业的名词可以使FetchPage()或者DownloadPage()。

    1.2 找到更优表现力的词

    下面是一些例子,这些单词更有表现力,可能适合你的语境:

    单词 更多选择
    send deliver、dispatch、announce、distribute、route
    find search、extract、locate、recover
    start launch、create、begin、open
    make create、set up、build、generate、compose、add、new
    1.3 避免像tmp和retval这样泛泛的名字

    使用像tmp、retval和foo这样的名字往往是“我想不出名字”的托辞。

    建议
    retval这个名字没有包含很多信息。用一个描述该变量的值的名字来代替它。

    然而,在循环迭代器中,像i、j、iter等名字常用作索引和循环迭代器。尽管这些名字很空泛,但是大家都知道它们的意思是“我是一个迭代器”。

    1.4 用具体的名字代替抽象的名字

    例如,假设你有一个内部方法叫做serverCanStart(),它检测服务是否可以监听某个TCP/IP端口。然而serverCanStart()有点抽象。canListenOnPort()就更具体一些。

    1.5 为名字附带更多信息

    我们前面提到,一个变量名就像是一个小小的注释。下表给出更多需要给名字附加上额外信息的例子:

    情形 变量名 更好的名字
    一个“纯文本”格式的密码,需要加密后才能进一步使用 password plaintext_password
    一条用户提供的注释,需要转义之后才能用于显示 comment unescaped_comment
    已转化为UTF-8格式的html字节 html html_utf8
    1.6 在小的作用域里可以使用短的名字

    作用域小的标识符不用带上太多信息,因为所有信息(变量的类型、它的初值、如何析构等)都很容易看到,所以可以用很多的名字。

    1.7 利用名字的格式来传递含义

    例如在Java中,通常以字母全部大写加下划线的形式表示常量(CONSTANT_NAME)。再比如,给jQuery返回的结果通常会加上$作为前缀。

    2. 不会误解的名字

    关键思想
    要多问自己几遍:“这个名字会被别人解读成其他的含义吗?”要仔细审视这个名字。

    2.1 推荐用min和max来标识(包含)的极限

    加入你的购物车应用程序最多不能超过10件物品:

    MAX_ITEM_IN_CART = 10
    
    if shopping_cart.num_items() > MAX_ITEM_IN_CART:
        Error("Too many items in cart.")
    
    2.2 推荐用begin和end来表示包含/排除范围

    因为对begin/end的使用是如此常见,至少在c++标准库中是这样的,还有大多数需要分片的数组也是这样用的,它已经是最好的选择了。

    2.3 给布尔值命名

    当为布尔值变量或者返回布尔值的函数选择名字时,要确保返回true和false的意义很明确。通常来讲,加上像is、has、can、should这样的词,可以把布尔值变得很明确。

    2.4 与使用者的期望相匹配

    很多程序员都习惯了把以get开始的方法当作“轻量级访问器”这样的用法,它只是简单地返回一个内部成员变量。如果违背这个习惯很可能会误导用户。

    以下是一个用Java写的例子,请不要这样做:

    public class StatisticsConllector {
        public double getMean() {
            // Iterate through all samples and retuan total / num_samples
        }
    }
    

    在这个例子中,getMean()的实现是要遍历所有经过的数据并同时计算中值。如果有大量的数据的话,这样的一步可能会有很大的代价!但一个容易轻信的程序员可能会随意地调用getMean(),还以为是个没什么代价的调用。

    3. 审美

    好的源代码应当“看上去养眼”。使用好的留白、对齐及顺序可以让你的代码更容易阅读。
    确切地说,有三条原则:

    1. 使用一致的布局,让读者很快就习惯这种风格。
    1. 让相似的代码看上去相似。
    1. 把相关的代码行分组,形成代码块。

    4. 该写什么样的注释

    当你写代码时,你的脑海里有很多有价值的信息。当其他人读你的代码时,这些信息已经丢失了,他们所见到的只是眼前的代码。

    关键思想
    注释的目的是尽量帮助读者了解得和作者一样多。

    4.1 什么不需要注释

    下面代码中的注释没有任何价值:

    # remove everything after the second '*'
    name = '*'.join(line.split('*')[:2])
    

    从技术上讲,这里的注释没有表达出任何新信息。不要为那些从代码本身就能快速推断的事实写注释。

    4.2 不要给不好的名字加注释——应该把名字改好

    注释不应该应用于粉饰不好的名字,我们完全可以用一个更加自我说明的名字。写代码的人常常把这条规则表述为:好代码>坏代码+好注释

    4.3 记录你的思想

    现在知道了什么不需要注释,下面讨论什么需要注释。
    很多好的注释仅通过“记录你的想法”就能得到,也就是那些你在写代码时有过的重要的想法。下面是一个例子:

    //出乎意外的是,对于这些数据用二叉树比用哈希表快40%
    //哈希运算的代价比左/右比较大得多
    

    这段注释教会读者一些事情,并且防止他们为无谓的优化而浪费时间。

    4.4 为代码中的瑕疵写注释

    代码始终在演进,并且在这过程中肯定会有瑕疵。不要不好意思把这些瑕疵记录下来。
    例如,当代码需要改进时:
    // TODO:采用更快的算法
    或者当代码没有完成时:
    // TODO(dustin):处理除JPEG以外的图像格式

    4.5 “全局观”注释

    对于团队的新成员来讲,最难的事情之一就是理解“全局观”,类之间如何交互,数据如何在整个系统中流动,以及入口点在哪里。设计系统的人经常忘记给这些东西加注释,“只缘身在此山中”。下面是一个文件级别注释的简单例子:

        // 这个文件包含一些辅助函数,为我们的文件系统提供了更便利的接口
       // 它处理了文件权限及其他基本的细节。
    
    4.6 总结性注释

    全局观注释代表文件级别的注释,就算在一个函数的内部,写一个总结性的注释也是个不错的注意,使读者不至迷失在细节中。这段注释巧妙的总结了其后的底层代码:

    #Find all the item that customers purchase for themselves
    for customers_id in all_customers:
        for sale in all_sales[customer_id].sales:
            if sales.recipient == customer_id:
                ....
    

    相关文章

      网友评论

          本文标题:《编写可读代码的艺术》读书笔记(一)

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