1. 1. 命名的基础
  2. 2. 少用get
  3. 3. 不用do
  4. 4. 用情态动词搭配Boolean返回值函数
  5. 5. 注意上下文是否已暗示主语
  6. 6. 不在函数名中描述参数
  • 总结
  • 本文为 一旬一题写作计划 重构 专题内文章,读完本篇大约需要4分钟。


    合理正确的命名简直是编程时最常头疼的问题之一(仅次于写文档),我的强迫症同事每次写新需求的代码时候都要为此纠结半天。
    我们会使用像下面这种,大家提到最多的,类似“格式规范”的命名规范:
    Google Java编程风格指南中文版
    Code Style for Contributors
    这种规范规定了命名的格式,大大降低了代码混乱度,但是对可读性的贡献却还不够。
    好的代码应该表达出自己的功能,望文知义是我们的追求。

    1
    2
    3
    4
    5
    /*能读,不好读*/
    houzi.food(b) // the money eats a banana

    /*望文知义,与自然语言(右侧的注释)很接近*/
    monkey.eat(banana) // the money eats a banana

    我们开发中可能60%的时间都花在命名和阅读代码上了,如何让代码更“可读”(更有语义),是个值得花时间琢磨的问题。这篇文章将会介绍些让命名更具语义的实践。

    命名的基础

    命名必须传递足够的信息。
    通常,对象代表一个事物概念,我们使用名词来命名。用名词的复数或加上 List 、 Map 后缀来命名集合。函数都是用来做事情的,函数的名字应该由动词开始。
    于是我们建立了最基础的规则:名词命名对象,动词命名函数。

    少用get

    我们编写的大多数函数有个返回值,所以用动词get开头特别常见。对于简单访问对象属性的方法,get还是蛮适合的,比如POJO和JavaBean的数据获取方法。
    但是多数情况下,get并不是一个好的选择。想象有一个方法,它会连接到远程服务器,搜索某个数据集,并将其排序后返回。我们可以把它叫做 getData,但这样的命名就没能提供足够的信息,跟从本地获取数据或者简单访问JavaBean没有任何区别,读者也无法猜测这个函数会做出些什么。如果换成fetchUserInfoAsync()之类的,函数的目的就一目了然了,读者得到了函数做了更多的工作来提取数据的暗示。
    我们可以提炼出一条新规则,即时直接访问数据时用get,其他更复杂的情况应当另选更精确的动词。比如做数学运算得出一个返回值的函数,calculate 会比 get 合适。

    试下给下面的函数选个动词。

    1
    2
    3
    4
    5
    6
    7
    Item getItem(Item match) {  
    for(Item temp : itemList){
    if(temp.values == match.values){
    return temp;
    }
    }
    }

    看看这个函数干了什么,不是简单地获取某个数据,它在集合中搜索某个符合条件的元素并返回。searchItem或者findItem都是容易想到而且很不错的选择。试下描述函数的功能,从中摘取动词,就是这么简单。

    不用do

    有的人喜欢用do命名,仔细想想,每个函数都有do一些事情,do 几乎不能提供任何有用信息。类似的词语还有handle, perform, return。

    用情态动词搭配Boolean返回值函数

    Boolean返回值函数最适合用 is, are, was, were开头,或者搭配情态动词。

    情态动词(Modal verbs)本身有一定的词义,表示语气的单词。但是不能独立作谓语,只能和动词原形一起构成谓语。情态动词用在行为动词前,表示说话人对这一动作或状态的看法或主观设想。

    也就是can, could, may, might, must, shall, should, will 和 would,这些词隐含了假设当前条件为真的反问信息。比如Button.isEnabled(),User.canAccess()。

    注意上下文是否已暗示主语

    Java语言中,方法是某个类里面定义的一个函数,也就是方法跟类是有绑定关系、在类或者对象的上下文中运行。所以有时候函数名中没必要再描述主语。比如一个保存信息的方法,可以叫做saveMessage();假如这是Message类的一个方法,就可以简化为save()。你可以对比感受下Message.save()和Message.saveMessage()。

    不在函数名中描述参数

    函数的签名本身就告诉了读者有什么参数,再在方法名中描述就多余了。findUserByUserIdAndToken(String userId, String token) 可以简化成 findUser(String userId, String token)。

    总结

    • 名词命名对象,动词命名函数。
    • 少用动词get
    • 避免使用do之类含糊不清的动词
    • 用情态动词搭配Boolean返回值函数
    • 不在函数名中描述参数
    • 从上下文和函数具体完成的任务出发,选用准确的描述

    虽然不是必要的,但如果遵循这种语义规范(况且也不难),能让我们的代码很清楚地表达出它的关键逻辑。重构过代码的同学应该明白这种代码的自描述性有多重要。

    任何一个傻瓜都能写出计算机可以理解的代码,唯有写出人类容易理解的代码,才是优秀的程序员。
    ——《重构:改善既有代码的设计》

    错误代码的影响力是有限的,bug很快会遁形;但能正确执行的无法理解的混乱代码却会造成长远的负面影响。多花一丢丢时间去提升代码的清晰度,后面的开发才会更愉悦。

    参考资料与扩展阅读

    Semantic method naming

    编写「可读」代码的实践

    Google Java编程风格指南中文版

    Code Style for Contributors