名正则言顺:聊聊“起名”这件事
昨天练功房里讨论的话题是:编程的时候,关于“起名”这件事,有什么想法。
LW同学分享了自己的一个小经验:
比方说有三个函数,分别用来获取三种订单信息 第一种取名方式:getFooOrder,getBarOrder,getOtherOrder 这种取名方式感觉正规一点,也符合英语语法 但是我可能更倾向于第二种取名方式:getOrderFoo,getOrderBar,getOrderOther 这样取名的原因,一个是看起来名字都整齐一点,再一个就是IDE提供的函数名列表一般是按字母排序的,这样子可以一眼看到类中具体有几种订单之类的信息。
YQ同学提到自己的一个困惑:
比如说…前端里常见的响应一个 scroll 下滑的操作函数
就取名 handleScrollDown
就感觉好长一个😂
或者类似 handleConfirmButtonClick
Kimmy同学适时放了个嘲讽:
ExtensibleMarkupLanguageHyperTextTransferProtocol(
然后大家都觉得这个名字长度其实还行,可以用“on+事件名”的形式,还能稍微再短点。
《实现模式》里面讲了一系列关于命名的建议。比如说,对于变量的命名,Kent Beck给出的建议是“按角色命名”:
我用变量名来描述它扮演的角色。其他关于这个变量的重要信息——生命周期、作用域和类型——通常从上下文中就可以找到。 Kent Beck,《实现模式》
再取变量中的一种——局部变量——来说,它常扮演的角色有几种:
解释(Explaining):面对复杂的运算时,把运算的中间结果赋值给一个局部变量,帮助阅读者分步理解整个运算。
复用(Reuse):如果一个表达式的值会不断变化(例如“当前的时间”),或者取得这个值的成本很高(例如需要调用网络接口),而你又需要多次使用同一个值,就应该将其保存在局部变量中,以便重复使用。
收集器(Collector):用来收集稍后需要的信息。如果要将收集器作为函数返回值,就将它命名为
result
或results
。计数(Count):可以看作一种特殊的收集器,专门用于记录“个数”。命名时可以考虑
xxxCount
这样的名字。元素(Element):在迭代遍历一个集合时,需要用局部变量指代集合中的每一个元素。这种局部变量可以直接命名为
each
,或者在each
后面加上集合的名字,即eachXxx
的形式。
这里面的前两类角色需要根据逻辑概念来命名,后三类角色命名都是有套路的。如果发现一个局部变量不是这几类角色,很可能就需要想一想,是不是正确地在使用局部变量。
我在翻译《重构》第二版的时候跟林从羽聊到一个问题:同样是写程序,业余选手很可能是想到哪儿写到哪儿,要解决什么问题的时候临时想需要什么元素;而职业选手的脑子里则有一个可以穷尽的模式列表,什么情况下使用什么程序元素对他来说不是一个启发式的(heuristic)思考过程,而是模式匹配的过程。这个过程第一很快,第二不会写出稀奇古怪的、费解的程序结构。
可能职业程序员和业余程序员的区别就在于,业余选手需要heuristic的很多场合,对于职业选手来说是可以穷举的模式匹配。
(类似的例子,比如“一段代码好不好”这个问题,业余选手要看着琢磨半天,职业选手则是在脑子里迅速把24个坏味道模式匹配了一遍,给出一个答案。)
又跑题了……说回“起名”这件事。我以前同事李光磊同学写过一段测试代码,被大家夸了好长时间 ,就像这样的……(感兴趣的同学可以去搜索InfoQ的文章《TDD实践之实用主义》)
这个玩法最大的好处是,生成的测试报告看着很清晰,单元测试的报告可以直接当文档来看,真正实现了“活文档”。
然后CF同学放了个大招,不光测试方法名可以中文,测试步骤也可以中文。BDD流派说测试用例(不仅仅测试报告)就是文档,这个文档看着怎么样?
于是我终于按捺不住喜悦的心情,找出了一个十六年前的超级大招……是C++之父Bjarne Stroustrup在一篇叫做《扩展C++2000的重载规则》的文章里写的……
高不高级?厉不厉害?
嗯,这篇文章是Stroustrup发表于2001年4月1日,然后我翻译的版本发表于《程序员》2002年4月刊😼
🏋🏻 程序员练功房🏋🏻