如何写出少bug的代码,你需要这4点建议
标签: 结构化代码 减少BUG的建议 优化代码 优化代码的4点建议 代码设计原则
- 原文: These four “clean code” tips will dramatically improve your engineering team’s productivity
- 译者: Fundebug
为了保证可读性,本文采用意译而非直译。另外,本文版权归原作者所有,翻译仅用于学习。
小编推荐:Fundebug专注于JavaScript、微信小程序、微信小游戏,Node.js和Java线上bug实时监控。真的是一个很好用的bug监控服务,众多大佬公司都在使用。
我们曾经是这张图右边的情况,现在已经向左侧移动。
几年前,我们在VideoBlocks的时候遇到过一个很大的关于代码质量的问题:像意大利面“spaghetti”一样混乱的逻辑,大量的重复代码,没有测试等等。开发一个新的功能、甚至修复一个小小的bug都会一肚子气,需要吃胃药来平复。每分钟都在无数次谩骂”WTF“。
今天,我们整个codebase的代码质量有了明显的提升。这要归功于我们可以去花时间花力气来维护代码的质量。之前当我们遇到代码质量问题的时候,我们整个团队都认真阅读了Robert Martin的一本书Clean Code,然后尽力尝试用他建议的方式。我们甚至为整个开发团队树立了clean code的文化。如果你的公司已经开始规模化,我强烈建议你两项都做。在团队中实现”clean code“的实践,长期来看是可以双倍提高生产率的,并且整个团队不再会满口脏话。
我从Clean Code和其它资源中梳理出来的几点建议:
- 如果没有经过测试,代码不合格
请编写大量的测试,特别是单元测试,否则你会后悔的。 - 使用有意义的名字
使用短小且精确的名字来命名变量、类和函数。 - 类和函数要足够小,遵循单一职责原则(SRP)
函数不要超过4行,类不要超过100行。对的,你没有看错。它们应该足够小,只做一件事情。 - 函数不应该有副作用
什么叫副作用呢?修改传入参数的值。这个做法很危险。因此,你要确保没有这么做。
接下来我们一个一个详细过一遍。
1. 如果没有经过测试,代码不合格
在工程师们遇到bug并且它们本应该被测试发现的时候,我经常性地重复这句话。你需要反复去灌输这个理念,在公司树立测试的文化。编写大量的测试,特别是单元测试。尽量去尝试各种可能的集成测试来确保在核心业务方面有足够的测试。
请反复灌输”如果没有经过测试,代码不合格”,直到他们完全实践。要将布道真正实践,不管他是一个新手还是有经验的老手。
2. 使用有意义的名字
在计算机科学中有两大难题:缓存验证和命名。
你也许之前听过这个说法。作为一名软件工程师,肯定感受颇深。如果你和你的团队不擅长合理地命名,最终代码会变得无法维护。工程师会无法继续忍受,甚至业务无法继续。
很认真地告诉你,朋友之间肯定不会随意用难听的名字来命名,就像data,foobar,或则myNumber。绝对不允许给类取SomethingManager这样的名字。除非有命名冲突,请确保你的命名简短又精确。在代码review的时候,跨文件查找名字可以很容易找到对应的函数。
3. 类和函数要足够小,遵循单一职责原则(SRP)
小和SRP就像鸡和鸡蛋。我们先来解释为何要小。
对于函数来说,什么叫做小?不超过4行代码。是的,你没看错,4行。你也许认为是瞎扯淡,打算立马关掉该窗口。我建议你看完这段再做决定。你也许没想到或则意识到可以写这么小的函数。不过呢,4-行函数可以促使你努力去思考,如何选择子函数的名字来确保整个代码可读性强。另外,也就是说你不能使用嵌套IF语句。
我们来看一个例子。Node有一个叫”build-url”的npm模块。该模块的用途已经在名字中充分表达。下面是代码:
|
该函数总共有35行。要理解这个函数并不是很难,不过如果使用我们的”small“法则,会更加易读。
|
你会发现,我们并没有严格遵守4行函数的原则,我们确实构建的函数都相对比较小。每一个只负责一项工作,而且根据函数名就很容易理解其含义。你甚至可以对每一个函数进行独立的单元测试。按照原来的写法,你需要去测试那个拥有35行代码的大函数。你也许注意到了,通过这种方式写出来的代码量略大,55行,超过35行。但是55行的代码更加具有可读性,更加容易维护。
那么如何写出这样的代码呢?从我个人经验的角度,最容易的方法是:首先把你要完成的任务用列表的形式记下来。那么每一个步骤都可能适合编写子函数。比如,我们可以将buildUrl函数分解为:
- 初始化base url和options
- 添加path,如果存在的话
- 添加query参数,如果有的话
- 添加hash(#),如果需要的话
你就会发现,其实每一个步骤都可以直接转化为一个子函数。一旦你熟悉了这个套路,你就会按照自上而下的方式来拆分一个大的任务,你会创建一系列的操作步骤,然后递归地去拆分每一个子步骤。
接下来我们来说单一职责原则,什么意思呢?摘自维基百科:
单一职责原则是一个计算机编程原则,每一个模块或则类应该只负责软件的一个部分的功能,并且该功能可以完全有这个类来封装。所以相关的服务都可以通过它来输出。
Robert Martin在Clean Code中提出了另一种定义:
一个类或则模块只应该因为一个原因而改变。(The SRP states that a class or module should one, and only one, reason to change.)
比如我们要打造一个系统来输出某种类型的报告并且展示出来。一个简单的方法是在一个模块里面存储报告数据,并且实现展示的逻辑。但是这个违反了单一职责原则因为我们有两个理由去改变它。首先,如果我们要更改报告某些域,我们需要更新代码;其次,如果我们要更改呈现的方式,我们也要更新代码。也就是说,我们应该把这两个功能分成两个模块来实现,比如”ReportData”和”ReportDataRender”。
4. 函数不应该有副作用
副作用是魔鬼,它会让你找不到bug在哪里。请仔细阅读下面的代码,并指出副作用是什么?
函数的命名直观易懂,通过邮箱和密码来查找用户,一个标准的网页应用的操作。不过,如果不阅读具体源代码。,它有一个隐藏的副作用你甚至不会注意到:登录用户!会创建一个登录token,加到数据库中,并返回一个cookie。
这样做是不对的,而且很多地方都不对。
首先,函数的接口不清晰。函数名表达的是获取用户信息,但是函数实现却把登录操作给做了。即使可以在文档中描述这个副作用,但不是很好。工程师都习惯使用IDE集成的intellisense工具来自动补全函数,根本不会注意到文档中描述的副作用。
其次,因为有太多的依赖,函数测试也不好做。你需要mocking HTTP请求,并且处理登录token。
再次,将查找和登录如此紧密的耦合很难满足所有可能的需求状况。在未来的业务上如果有变动,那么可能需要将它们独立拆开。
总的来说,记住我上面提到的四大法则并使用起来吧。
- 如果没有经过测试,代码不合格
- 使用有意义的名字
- 类和函数要足够小,遵循单一职责原则(SRP)
- 函数不应该有副作用
智能推荐
restful+ci框架 实践
restful架构: 是就是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。具体理论请看我上一篇写的restful理论。本篇主要记录下关于restful的实践。 restful实践: 工具: 这次在ci框架+restful 主要文件: 在控制器中添加控制器类:Restful.php。 在头部包含REST_Controller.php文件并继承...
Configuration, ConfigurationProperties和EnableConfigurationProperties用法
最近刚刚解决了个错误,突然又发现这个类在spring容器中找不到, 于是我就加一个 @Component的注解,哈哈直接启动成功,那我如果吧这个注解去掉,加上一个@Configuration的注解呢,哈哈还是可以的,毕竟里面已经有这个@Component的注解了。所以我就整理下Configuration,ConfigurationProperties,EnableConfigurationProp...
备战蓝桥杯--贪心算法刷题整理5
翻硬币(贪心算法) 看了一下网上的题解,感觉挺强,网友的做题思想值得借鉴,这里分享一下网友的链接,同时再分享一下自己的解题方案 链接:https://blog.csdn.net/qq_34594236/article/details/60326782 题目描述: 小明正在玩一个“翻硬币”的游戏。 桌上放着排成一排的若干硬币。我们用 * 表示正面,用 o 表示反面(是小写字母...
部署高可用RabbitMQ
安装 准备工作 这里我们使用三个RabbitMQ节点: 开通端口(具体见官方文档): 安装ErLang和RabbitMQ Server 安装文档见:https://www.rabbitmq.com/install-rpm.html。 采用RPM包而不是Repo的安装命令如下(以下的版本号可根据实际情况修改): 安装管理插件 安装文档见:https://www.rabbitmq.com/manage...
Opencv常用代码总结
文章目录 读取显示图像 保存图片 查看图片信息 读取视频 截取部分图像数据 颜色通道提取、融合与保留 边界填充 数值计算 图像融合 图像阈值 图像平滑(降噪) 形态学-腐蚀操作 形态学-膨胀操作 开运算与闭运算 梯度运算 礼帽与黑帽 图像梯度 Sobel算子 Scharr算子 laplacian算子 Canny边缘检测 图像金字塔 高斯金字塔:向下采样(缩小) 高斯金字塔:向上采样(放大) 拉普拉...
猜你喜欢
Numpy实现LDA
LDA与PCA的区别如下表: LDA的原理如下: 代码实现如下,这里使用的a,b是Nx2的二维点集合,经过LDA后,二维的点变为一维。更高维度的也是可以做到的。函数里的dim是原始数据的维度,d是想要降到的维度。 初始的数据如下图,红色点和蓝色点代表不同的分类。 经过LDA后,投影的一维数值如下图所示。 可见LDA实现了降维,而且两种分类的间距较大,类内的散度较小。...
Java反射机制
相关类型: java.lang.Class java.lang.reflect.Constructor java.lang.reflect.Field java.lang.reflect.Method java.lang.reflect.Modifier 作用: 1、反编译 .class –> .java&n...
Linux(Centos7)安装oracle12c
第一步:到oracle官网上下载oracle12c的镜像文件 第二步:添加用户和组 用普通用户登录后,打开命令行工具(terminal) 转到root用户 [vmtest@localhost ~]$ su root Password: 添加组 [root@localhost vmtest]# groupadd dba 添加用户 [root@localhost vmtest]# useradd or...
bootstrap中表格内容过长成省略号,鼠标悬停表格显示全部内容【页面标签中实现】
页面效果展示: 1. 先判断该对象是否存在,再使用freemarker的list语法进行遍历 2. <#if> 中对象.属性值的非空判断和字符串长度的判断,必须得有非空判断,否则报错 3. 字符串的截取显示, <td title=”悬浮显示的值”> 4. 页面代码展示...
