工程实践:给函数取一个"好"的名字
工程实践:给函数取一个"好"的名字
早在2013年,国外有个程序员做了一个有意思的投票统计(原始链接请见:《程序员:你认为最难做的事情是什么?》),该投票是让程序员从以下几个选项中选出平时在工作中自己认为最难做的事情:
- 做项目方案设计
- 编写测试用例
- 撰写设计文档
- 向别人解释我们在做什么事情
- 实现你不认同的feature
- 在别人写的代码基础上做改造
- 与人沟通
- 给函数、变量命名
- 进行工作量估时
也许在大家的印象中,撰写设计文档和在别人写的代码基础上做改造应该是最难的事情。但是最终的投票结果确让大家意想不到,这次投票一起有4522名程序员参与了投票,排在第一位的是给函数、变量命名:
大概一半的人投票给了给函数、变量命名,从这次投票结果我们足可以看出:给函数、变量命名虽然是一件再普通不过的事情,但是要想把这件事做好绝非易事。那么今天,我们就来聊一聊如何给函数取一个好的名字。以下是本文大纲目录:
一.常见的函数命名风格
二.函数命名的最高境界
三.函数命名最佳实践
若有不正之处请多多谅解,并欢迎批评指正。
请尊重作者劳动成果,转载请标明原文链接:
https://www.cnblogs.com/dolphin0520/p/10567879.html
一.常见函数命名风格
目前来说,最常见的函数命名主要有两种风格:驼峰命名和帕斯卡命名。
- 驼峰命名:多个单词组成一个名称时,第一个单词全部小写,后面单词首字母大写;如:
public void setUserName(String userName);
- 帕斯卡命名:多个单词组成一个名称时,每个单词的首字母大写;
public void SetUserName(String userName);
两种命名风格都是ok的,但要保证一点,对于一个团队或者一个项目,需要根据语言本身的推荐命名方式做好约定。比如java一般都采取驼峰命名,C#采取帕斯卡命名。
二. 函数命名最高境界
我们通常说:天下武功,唯快不破。那么对于函数命名来说最高境界是什么呢?我认为是:见字如面,顾名思义,就是看到函数的名字就知道这个函数具体做了哪些事情。
比如上面的函数:
public void setUserName(String userName);
但是下面这个函数命名就不是一个好的命名:
public String addCharacter(String originString, char ch);
这个函数,一咋看,还不错,从函数字面意思看是给某个字符串添加一个字符。但是到底是在原有字符串首部添加,还是在原有字符串末尾追加呢?亦或是在某个固定位置插入呢?从函数名字完全看不出来这个函数的真正意图,只能继续往下读这个函数的具体实现才知道。
而下面这几个名字就比上面要好得多:
public String appendCharacter(String originString, char ch); // 追加到末尾
public String insertCharacter(String originString, char ch, int insertPosition); // 插入指定位置
三. 函数命名最佳实践
1)要领1:动词选取要精准
通常来说,动词决定了一个函数要采取什么"动作"。动词取的好,一个函数名字已经成功了80%。
常用动词表:
类别 | 单词 |
添加/插入/创建/初始化/加载 | add、append、insert、create、initialize、load |
删除/销毁 | delete、remove、destroy、drop |
打开/开始/启动 | open、start |
关闭/停止 | close、stop |
获取/读取/查找/查询 | get、fetch、acquire、read、search、find、query |
设置/重置/放入/写入/释放/刷新 | set、reset、put、write、release、refresh |
发送/推送 | send、push |
接收/拉取 | receive、pull |
提交/撤销/取消 | submit、cancel |
收集/采集/选取/选择 | collect、pick、select |
提取/解析 | sub、extract、parse |
编码/解码 | encode、decode |
填充/打包/压缩 | fill、pack、compress |
清空/拆包/解压 | flush、clear、unpack、decompress |
增加/减少 | increase、decrease、reduce |
分隔/拼接 | split、join、concat |
过滤/校验/检测 | filter、valid、check |
动词决定了函数的具体动作,而名词决定了函数具体的操作对象,对于名词,尽量使用领域词汇,不要使用生僻或者大家很少使用的词语。
2)要领2:名词使用领域词汇
举个例子:集合的容量通常用capacity、集合实际元素个数用size、字符串长度用length,这种就遵循大家的使用习惯,不要用size去形如字符串的长度。
再比如,假如使用到建造者模式,那么通常会用build作为函数名字,这个时候就不要另辟蹊径,用create来作为函数名字,使用大家约定俗成的命名习惯更容易让你的代码被别人读懂。
常用名词表:
类别 | 单词 |
容量/大小/长度 | capacity、size、length |
实例/上下文 | instance、context |
配置 | config、settings |
头部/前面/前一个/第一个 | header、front、previous、first |
尾部/后面/后一个/最后一个 | tail、back、next、last |
区间/区域/某一部分/范围/规模 | range、interval、region、area、section、scope、scale |
缓存/缓冲/会话 | cache、buffer、session |
本地/局部/全局 | local、global |
成员/元素 | member、element |
菜单/列表 | menu、list |
源/目标 | source、destination、target |
3)要领3:函数取名最忌讳"名不副实"
函数取名最忌讳的是"名不副实",举个例子,假如有个Cache类,里面有个函数判断key是否过期:
public boolean isExpired(String key) {
// 当前时间戳
long curTimestamp = DateUtils.nowUnixTime();
// 获取key的存入时间戳
long storeTimestamp = getStoreTimestamp(key); if (curTimestamp - storeTimestamp > MAX_EXPIRE_SECONDS) {
// 注意这个地方的delete是个隐藏逻辑
delete(key);
return true;
}
return false;
}
上面这个函数从函数字面意思看是判断key是否过期,但是!!它居然在函数里面隐藏了一段特殊逻辑:如果过期则删除掉key。这个就是典型的"名不副实",这个是最忌讳的,会给后续的开发人员留下"巨坑"。
有两种方式去优化这段代码:
- 方式一:将隐藏逻辑去掉
public boolean isExpired(String key) {
// 当前时间戳
long curTimestamp = DateUtils.nowUnixTime();
// 获取key的存入时间戳
long storeTimestamp = getStoreTimestamp(key); if (curTimestamp - storeTimestamp > MAX_EXPIRE_SECONDS) {
return true;
}
return false;
}
- 方式二:改变函数名字
public int deleteIfExpired(String key) {
// 当前时间戳
long curTimestamp = DateUtils.nowUnixTime();
// 获取key的存入时间戳
long storeTimestamp = getStoreTimestamp(key); if (curTimestamp - storeTimestamp > MAX_EXPIRE_SECONDS) {
return delete(key);
}
return 0;
}
4)要领4:多查询条件的函数名字谨慎使用介词by
我们平时在写查询接口时,假如有多个查询参数怎么办?每个通过by一起连接依赖?No!这绝对不是明智的方式。假如一开始产品的需求是通过学生姓名查询学生信息,写出来的可能是这样的函数:
public List<Student> getByName(String name);
然后突然又有一天产品提出了新的需求,希望同时可以通过姓名和电话号码来查询学生信息,那么函数可能变成这样了:
public List<Student> getByNameAndMobile(String name, String mobile);
接着,没过多久,产品又希望根据学生年龄来查询学生信息,那么函数可能变成这样了:
public List<Student> getByNameAndMobileAndAge(String name, String mobile, int age);
如果这样来给函数命名,那么你的噩梦大门即将打开。
通常比较好的做法是:
如果是通过主键id来查询,那么可以通过by来连接查询信息,比如:
public Student getByStudentId(long studentId);
如果是通过其他属性来查询,并且未来会存在多个组合查询的可能性,建议进行封装,比如:
public List<Student> getStudents(StudentSearchParam searchParam);
最后,建议大家平时在写代码过程中,不要怕在函数命名上耗费时间,一个好的函数命名在后期会大大减少你代码重构的成本,争取对函数命名做到"见字如面"~
推荐下海子刚开通的微信公众号,欢迎大家扫码关注:
工程实践:给函数取一个"好"的名字的更多相关文章
- 代码实测:给redis中的key取一个正确的名字多么重要
redis对写入的key长度有限制吗? 太长的key对性能有影响吗? key越长对性能影响越大? 如何评估键长度对性能的影响? talk is cheap, show me the code! 今天我 ...
- panguan(判官):一个自研的任务执行引擎的工程实践
来某厂接近半年了,几乎没写过C++代码,说实话还真的有点手生.最近刚好有一个需求,然而我感觉我也没有办法用C++以外的语言去实现它.于是还是花了几天时间用C++完成编码,这是一个简单的任务执行引擎,它 ...
- C++工程实践 一个开始
打算把C++工程过程中的一些总结写下来.从打算写这个内容到今天(2017/1/12)已经很多年了,但是一直没有想好如何写,反正就这样,有什么写什么吧. C++工程实践之所以难产,主要原因有几个 内容比 ...
- Go 在游戏行业中的工程实践
在今年 1 月由七牛云主办的 ECUG Con 十周年盛会上,真有趣技术总监陈明达带来了题为< Go 在游戏行业中的工程实践>的精彩分享,深入讲解了 Go 的工程经验,错误和异常处理,in ...
- AES加密原理和AOE工程实践
在AI业务的开发的过程中,我们常常需要对模型文件进行加密.我们从以下几个方面来说一说AES的加密原理以及AOE里的工程实践. 常见的加密算法 AOE对模型加密需求的思考 AES的加密原理 AOE工程实 ...
- webpack 从入门到工程实践
from:https://www.jianshu.com/p/9349c30a6b3e?utm_campaign=maleskine&utm_content=note&utm_medi ...
- PHP网络爬虫实践:抓取百度搜索结果,并分析数据结构
百度的搜索引擎有反爬虫机制,我先直接用guzzle试试水.代码如下: <?php /** * Created by Benjiemin * Date: 2020/3/5 * Time: 14:5 ...
- 基于menu小插件探索工程实践
目录 一.准备工作 1.C/C++环境搭建 2.VSCode的配置 (1) 安装插件: (2) 设置配置文件: 二.工程化编程实战 1.模块化设计 2.可重用设计:进一步抽象 menu的进一步优化 可 ...
- Entity Framework 6 Recipes 2nd Edition(11-1)译 -> 从“模型定义”函数返回一个标量值
第11章函数 函数提供了一个有力代码复用机制, 并且让你的代码保持简洁和易懂. 它们同样也是EF运行时能利用的数据库层代码.函数有几类: Rowset Functions, 聚合函数, Ranking ...
随机推荐
- PAT1115:Counting Nodes in a BST
1115. Counting Nodes in a BST (30) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Y ...
- MAMP环境下为Mac OSX安装设置PHP开发环境
一.简单介绍: PHP 页需要通过 Web 服务器处理.因此,要在 PHP 进行开发,您需要访问支持 PHP 的 Web 服务器和 MySQL 数据库.phpMyAdmin 也很实用,它是 MySQL ...
- MySQL复制入门
Ⅰ.复制类型 1.1 逻辑复制 记录每次逻辑操作 主从数据库可以不一致 1.2 物理逻辑复制 记录每次对于数据页的操作 主从数据物理严格一致 基于重做日志 说明: 如果一个块(页)修改了,就把这个修改 ...
- PHP写的爬虫,爬指定网站页面上的各种图片
打算用php实现一个爬虫,这是爬指定页面的图片的一段程序,其他的部分还没调试好,先把这个放上来 <?php $string=file_get_contents("http://www. ...
- 在线OJ使用总结(acm)
赛码网OJ规则 用readLine()代替read_line() 用readLine()代替read_line() 用readLine()代替read_line() 用readLine()代替read ...
- api_request.go
&& !strings.HasPrefix(endpoint, "https") { endpoint, err = httpsEndpoi ...
- BZOJ_1391_[Ceoi2008]order_最大权闭合子图
BZOJ_1391_[Ceoi2008]order_最大权闭合子图 Description 有N个工作,M种机器,每种机器你可以租或者买过来. 每个工作包括若干道工序,每道工序需要某种机器来完成,你可 ...
- BZOJ_1097_[POI2007]旅游景点atr_状压DP
BZOJ_1097_[POI2007]旅游景点atr_状压DP 题面描述: FGD想从成都去上海旅游.在旅途中他希望经过一些城市并在那里欣赏风景,品尝风味小吃或者做其他的有趣 的事情.经过这些城市的顺 ...
- BZOJ_4002_[JLOI2015]有意义的字符串_矩阵乘法
BZOJ_4002_[JLOI2015]有意义的字符串_矩阵乘法 Description B 君有两个好朋友,他们叫宁宁和冉冉.有一天,冉冉遇到了一个有趣的题目:输入 b;d;n,求 Input 一行 ...
- angular开发环境配置全套教程
第一步.安装node.js 1.需要下载的文件路径:https://nodejs.org/en/download/ 2.安装nodejs: 3.验证node.js 4.npm安装以及验证: npm的安 ...