给代码起个好名字
在公司里,我有个不怎么常用的绰号,叫“算命先生”——帮别人起名字的,准确说,帮别人的代码起名字,包括项目名,目录名,类名,属性名,方法名,变量名等。事实上,我也确确实实帮过别人起名字,起名字总归有些套路,要避开一些坑,一个好的名字就是一个成功的开始,反之可能后面会带来很多困扰。我跟同事说,好的名字让你行走江湖更容易,你看“叶孤城”、“西门吹雪”、“东方不败”这些名字一听就知道是绝世高手,但你试着叫“王霸天”、“李二狗”、“牛春花”,不是活不过两集就是连上镜的机会都没有。
下面分享一些个人的经验及看法,所有的观点都并非绝对,当你看完整篇文章后肯定也深以为然,我所提出的这些套路也是为了这么个中心服务的: 使得代码更有条理和可读性。
1,用英文,别用中文,别用拼音,更加别用拼音缩写
这应该是老生常谈了。
C#/Java语言挺神奇,可以用中文做标识符,比如你创建一个类,叫“货物订单”,完全没问题,但有人尝试过之后就很快放弃了,因为可读性实在太差了,另外在做代码搜索的时候,输入中文本身就比英文要慢,英文还方便用正则等去匹配,中文在这方面操作起来就比较困难,如果你还想把代码移植到别的语言去,就更加不能用中文了。
拼音也别用,因为拼音你不念出来的话你往往不知道它想表达什么意思,汉语拼音中还有声调,而代码中又表示不了,有时候真能让阅读者一脸懵逼。
那拼音缩写就更加不用说了,我维护过一些老古董代码就全是拼音缩写,恶心+崩溃。
好 | 不好 | 不好 | 垃圾 |
GoodsOrder | 商品订单 | ShangPinDingDan | Spdd |
InventoryCheck | 库存盘点 | KuCunPanDian | kcpd |
InvoiceManagement | 发运单管理 | FaYunDanGuanLi | fydgl |
RouteConfiguration | 路由配置 | LuYouPeiZhi | lypz |
2,不要拼写错误
这不是废话么?但也不知道是由于不小心还是英文水平太差,我遇到的代码中的拼写错误实在太多太多了,更有甚者把公司的名称都写错的。如果你哪天用微软的代码,发现命名空间是:Micorsoft.Extensions.Logging,你会有什么感受?
写代码和做别的事情一样,也需要用心,遇到不确定的东西时,查一下,问一下,如发现自己之前的问题,要及时修正。
随便列举一些拼写错误(跟技术无关,纯粹看英语水平和仔细程度)
错误 | 正确 | 描述 |
lable | label | 字母前后写错是常发生的事情 |
catched | caught | catch的过去分词为不规则 |
loging | logging | 注意添加-ing的特例 |
IsEnable | IsEnabled | Enabled才是形容词,才能用系动词 |
登陆 | 登录 | 你以为中文的错别字就少了? |
SingleBox | PackingList | 本公司的梗,“发票箱单”,某同事翻译成了Single(单)Box(箱),奇葩 |
3,用名词充当类名
类,通常表示一个数据实体,或者一系列数据与方法的封装,大多时候是应当使用名词的。下面是一些例子:
名称 | 不太好 | 不错 |
登录信息 | Login(这是动词唉) |
LoginReq(Req表示Request,一看就知道这是个登录请求)
或LoginUi,后缀Ui表示UI层使用的类。
|
出库订单 | Order(太普遍了,且order是SQL的关键字,容易带来一些不便) | OutBoundOrder |
全局变量 | Global(这是形容词) | GlobalData,GlobalAppConfig等 |
总线请求类 | BusRequest(这是动词) |
BusRequester(请求者,凑合,但仍不太好)
BusReqManager(这个名字明显更好)
|
4,不要使用太普遍的名词充当类名
如Configuration,这个词表示配置,你打算创建这么一个类表示系统的配置信息,并提供相应方法。但你要注意了,一个较大的系统里通常有很多很多的配置,比如程序的全局配置,入库规则配置,出库规则配置,外部接口访问配置,日志配置……如果你全都叫Configuration,你很可能三天两头被自己搞懵。那怎么办?写具体点不就行了么?
不好 | 好一点 | 说明 |
Configuration |
GlobalApplicationConfiguration
InBoundConfiguration
ExteralApiConfiguration
LoggingConfiguration
|
起码更具体了 |
Param
(某个外部API的参数)
|
GenericExternalApiParam
|
标识符命名没必要再从技术上描述它是什么东西,正如你不需要写完“int i=1”后加个注释“定义整型变量i并赋值1”,要使得名字符合业务逻辑 |
当然,也有例外的情况,比如我的项目中通常有个类叫“Fmt”,非常简单,其实就是Format的简写,这个类不涉及到具体的业务逻辑,只是用来对时间日期和数值进行一些格式化操作,所以可以起个这么简的名字。
5,适当的简写与约定俗成的缩写
上面起的名字你是不是觉得太长了?那可不可以缩写一些?那是肯定的,代码中,我们存在着很多约定俗成的“单词”,举个最简单的例子:ID,ID是Identity的简写,为什么我们现在都知道?因为已经“约定俗成”了,这种例子还很多,我随便举一些:
原本 | 简写 |
participant | ptcp |
application | app |
description | desc |
abbreviation | abbr |
configuration | config或cfg |
Machintosh | Mac |
缩写的规则通常是取前面几个字母,但也有例外的,如前面提到的participant,如果缩写为part的话,由于part是另外一个单词,容易引起误会,所以就取participant中的几个与发音相关的辅音字母来组成简写,所以configuration也可以简写为cfg,关键是要大家都认可。
6,约定俗成的缩写
英文中的缩写实在太多了,如果没有这些缩写,用英语就简直无法交流,我并不夸张,随便写几个缩写的例子:
英文缩写 | 英文全称 | 中文 |
NASA | National Aeronautics and Space Administration | 美国国家航空和宇宙航行局 |
BASIC | Beginner's All-purpose Symbolic Instruction Code | 初学者通用符号指令代码 |
EPROM | Electrically Programmable Read-Only-Memory | 电可编程序只读存储器 |
JSON | JavaScript Object Notation | JavaScript对象表达式 |
ASN | Advanced Shipment Notice | 预到货通知单 |
DN | Delivery Number | 运单号 |
现在你有问题了:“你前面刚说拼音缩写‘垃圾’,为什么这里又允许英文缩写?”Good question,我这样说吧:关键点在于约定俗成,大家认可,英文中的缩写词经常能自己成为一个单词,如ROM,我们都直接念它“[rɔm]”,而不是“R-O-M”,大家都已经认可了“ROM”这个单词了,这就可以了。文章一开始我也说了,所有这些,都是为了这个中心服务的:使得代码更有条理和可读性!所以我提出的建议也只是建议,不是铁律。
这么说的话,我们是不是拼音缩写在某些时候也能用一下?——没错,我们公司经常就用Shwgq来表示“上海外高桥”,一来“上海外高桥”是专有名词,只能写拼音,二来公司同事都已经认可了,于是就可以愉快地使用了,但我又要说回来,这个是个特例,千万不要滥用。
7,驼峰命名法
先问这么一个问题:UserID好,还是UserId好?
也许你有你的看法,而我的看法很明确:UserId好,因为它很好地区分出了ID这个“单词”,前面说了ID是Identity的简写,但认可的人多了之后,ID本身就成为了一个单词,根据驼峰命名法的规则,单词首字母大写,其余小写,因此应当写UserId,可能一开始感觉有点不习惯,但后面很快就能适应。
另外还有一个问题:UserName好,还是Username好?
按规则应该是Username,因为这本身就是一个单词,但由于历史原因,在我的项目里面,一律写成UserName,以前写错了,惯性太大,改不了,就当是User和Name两个单词拼成的吧——看吧,兵无常势水无常形,要灵活应变。
另外还要注意一点,用驼峰命名法的时候,避免连续出现大写字母,否则很影响代码可读性。
8,适当使用后缀区分
有时候实在不知道怎么起名字,如登录,叫“Login”,这个也许表示客户端向服务器提交的登录请求,但也能表示服务器处理好登录请求后返回给客户端的信息, 怎么办?
我的办法是加上后缀作区分:
登录 | 登录请求 | 登录响应 |
Login | LoginReq | LoginResp |
这样就非常清晰了,Req即Request,Resp即Response,这种方法还能把Login这个动词变作一个名词,太好用了。
再比如,员工信息,叫EmployeeInfo,它可以是来自客户端的提交的信息,根据系统的分成架构,这个Model需要从UI层传递到业务逻辑层,但这两个层的EmployeeInfo类是有差别的,难道都要叫EmployeeInfo,只用命名空间来区分吗?我的“套路”一般如下:
员工信息 | UI层的员工信息 | 业务逻辑层的员工信息 |
EmployeeInfo | EmployeeInfoUi | EmployeeInfoBl 或 EmployeeInfo |
UI层加上Ui后缀(根据前面提到的规则,i小写),业务逻辑层可以加上Bl,也可以不加,因为UI层加了,就表示可以区分开来了。
9,用谓宾结构来命名方法
方法表示某个执行动作,通常都是谓宾结构,为什么把主语省掉了?因为主语100%是调用者,根本不用问。这是一些例子:
方法 | 好 |
报关单作废 | CancelDeclaration |
检验是否存在 | CheckIfExisting |
出库确认 | ConfirmOrder |
删除核放单 | DeleteGatebill |
没太多好说的,在动词的选择方面,有以下这些常用动词:
动词 | 英文 |
创建 | Create |
增加 | Add(注意跟“创建”语义上的差异) |
更新/编辑 | Update/Edit |
删除/移除 | Delete/Remove |
清空 | Clear/RemoveAll |
获取/设置 | Get/Set |
发送/接受 | Send/Receive |
检查是否XXX | CheckIf |
生成 | Generate |
计算 | Calculate |
上传/下载 | Upload/Download |
万能动词 | Do/Make |
还有很多,我不列了,否则就真的变成上英语课了,英语和汉语一样,同义词很多,但语义上常常会有些微小的差异,比如创建和增加,创建一个新用户,这是一个从无到有的过程,所以通常用Create,而将这个创建好的用户加到开发部门中去,则是Add的概念,开发部门本来就存在的,只是多了一个人,大家去体会体会。我们在选动词的时候通常会选比较符合英文文法的词,而不能太过于“技术”,如选择Create而不是SQL的Insert,因为Insert是个数据库技术中的概念,而不是业务逻辑上的概念,再如选择Send而不是Post,也是一样道理,但这个并不绝对,假如你开发的是一个网络访问的基础库,那么你完全可以用Post啊,因为这个情形下,Post本身就更能描述业务逻辑。
实在不知道怎么选动词的话可以考虑下Do和Make这两个万能动词,DoXxx,可以表示干任何事情,而Make一样很万能,Trump大帝竞选美国总统的时候口号不是“Make American Great Again”么?使或让的意思,放在很多场合都适用,再比如Make money,“赚钱”的意思。
10,复数与列表
有次我复查代码,看到一个“订单”中的“订单明细”是这么命名的:OrderLineCounts。莫名其妙,这根本不通啊。
用“Line”表示明细是我们公司的约定俗成,一个订单主档中带若干订单明细,我说用来表示这种明细列表通常有两种方法,一种是用复数,另一种加个List后缀:
不通 | 通 | 通 |
OrderLineCounts | OrderLines | OrderLineList |
这里还要注意一下:
1,不是所有名词都有复数形式,英语中有些词不可数对不?所以加个List后缀可能更通用一些
2,不是所有可数名词都可以直接加s变成复数形式,如Data,其复数形式应该是Datum,不要写Datas
3,有些单词单复数同形,如Goods,商品,复数也是Goods,不要写作Goodses
最后
讲一个东西,叫“破窗效应”(Broken Window Theory),我是许多年前在《程序员修炼之道》这本书上看到的(这本书强烈推荐一下),这个道理很简单,就是说如果你一开始对系统中出现的破败视而不见的话,更多的破败就会出现,直到事态不可挽回。你对代码的命名不认真,马马虎虎,导致同事看不懂,同事误会了你的意思,跟着你乱命名,你再次接手这代码的时候,已经凌乱不堪,你感觉已经很难维护,于是更加得过且过,直到程序没人再敢去碰。
“宇宙的熵在升高,有序度在降低,像平衡鹏那无边无际的黑翅膀,向存在的一切压下来,压下来。可是低熵体不一样,低熵体的熵还在降低,有序度还在上升,像漆黑海面上升起的磷火,这就是意义,最高层的意义,比乐趣的意义层次要高。”——《三体:死神永生》