您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

别纠结,提高代码整洁度也没那么难

时间:2020-07-14 12:41:36  来源:CSDN  作者:
别纠结,提高代码整洁度也没那么难

作者 | Jonathan Fulton

译者 | 弯月,责编 | 屠敏

头图 | CSDN 下载自东方 IC

出品 | CSDN(ID:CSDNnews)

以下为译文:

几年前,我们曾遇到过重大的代码质量问题:大多数文件中的逻辑纠缠夹杂、大量重复、没有测试。无论是编写新功能还是修复很小的bug都需要付出呕心沥血的代价,常常气到你吐血。令我们苦不堪言。

别纠结,提高代码整洁度也没那么难

如今,我们的代码库的整体质量明显提高了,这在很大程度上要归功于我们为提高代码质量而做出的不懈努力。几年前,在发现代码质量问题后,我们整个团队一起阅读了Robert Martin的《代码整洁之道》,然后竭尽全力贯彻了他的建议,甚至引入了“清洁规范”作为工程团队的核心文化。如果你打算扩张团队,那么我强烈建议你现在就开始实施这两项措施。从长远来看,恰当地实施“干净的代码”实践可以提高一倍生产力,并显著提高工程团队的士气。有了选择,谁还会愿意进入上图右边那个Bad code的房间呢?

在我们实施的“清洁规范”以及其他想法之中,有四项措施将团队的生产力和幸福指数提高了80%。

  1. 没有经过测试的代码一概不安全。

    你需要编写大量测试,尤其是单元测试,否则你会追悔莫及。

  2. 选择有意义的名称。

    为变量、类和函数选择言简意赅的名称。

  3. 类与函数保持最小,遵守单一功能原则

    函数不应超过4行,而类不应超过100行。是的,你没看错。而且它们应该只做一件事。

  4. 函数不能有副作用

    副作用(例如,修改输入参数)是有害的。请确保你的代码中没有副作用。尽可能在函数声明中明确规定这一点(例如,传入基本类型,或者传递没有setter的对象)。

下面我们来详细介绍上述每一点,帮助你理解并应用到工程团队的日常工作中。

别纠结,提高代码整洁度也没那么难

 

没有经过测试的代码一概不安全

每当遇到原本应该在测试捕捉到的bug时,我就会对我们的工程师重复这句话。除非你建立了测试的文化,否则你也会一次又一次地引用这句话。你需要编写大量测试,尤其是单元测试。认真考虑集成测试,并确保你的测试用例足够涵盖核心业务功能。请记住,如果有一段代码没有被测试覆盖到,那么将来肯定会出问题,而且你根本意识不到,直到被你的客户发现。

你需要一遍又一遍地向团队成员重复这句话:“没有经过测试的代码一概不安全”,直到这句话在每个人的心里生根。无论你是刚毕业的新手软件工程师还是经验丰富的资深软件工程师,都应该时刻履行这个实践。

别纠结,提高代码整洁度也没那么难

 

选择有意义的名称

计算机科学界有两大难题:缓存失效和命名。

可能你曾听过这句话,这与工程团队的日常工作有着莫大的关系。如果你和你的团队成员不擅长代码中的命名,那么你们的维护工作就会变成一场噩梦,而且你将一事无成。你会失去最优秀的开发人员,而且你的公司距离倒闭也不远了。

这个问题非常严重,你不应该使用诸如data、foobar或myNumber之类不恰当的变量名,而且也绝对不能将SomethingManager作为类名称。务必使用言简意赅的名称,确保在发生冲突时能准确找到。良好的命名不仅可以大幅提高开发人员的效率,而且还可以通过IDE的快捷方式“按名称查找” 等轻松查找文件。另外,良好的命名需要通过严格的代码审核贯彻。

别纠结,提高代码整洁度也没那么难

 

类与函数保持最小,遵守单一功能原则

这两大原则的关系就像鸡和鸡蛋一样,无论先有鸡还是先有蛋,有了二者的因果轮回,才有我们无尽的美味。下面先来谈谈类与函数保持最小。

“小”对函数意味着什么?不超过4行代码。是的,你没看错,就是4行。你可能现在就想告辞了,但是千万别走。虽然这个数字看起来有些武断,而且太少,你一辈子可能都没写过像这样的代码。但是,只有4行代码的函数会强迫你认真思考,并为子函数选择真正的好名字,而这些子函数就是代码最好的文档。另外,4行代码意味着你不会使用嵌套的IF语句,省得你需要耗费大量脑力搞清楚所有的代码路径。

下面让我们一起来看一个例子。Node有一个名为“build-url”的npm模块,用途如其名所示:构建URL。你可以通过这个链接(https://github.com/steverydz/build-url/blob/master/src/build-url.js)查看源文件。以下是相关代码。

function buildUrl(url, options) { var queryString = ; var key; var builtUrl;
 if (url === ) { builtUrl = ''; } else if (typeof(url) === 'object') { builtUrl = ''; options = url; } else { builtUrl = url; }
 if (options) { if (options.path) { builtUrl += '/' + options.path; }
 if (options.queryParams) { for (key in options.queryParams) { if (options.queryParams.hasOwnProperty(key)) { queryString.push(key + '=' + options.queryParams[key]); } } builtUrl += '?' + queryString.join('&'); }
 if (options.hash) { builtUrl += '#' + options.hash; } }
 return builtUrl;};

请注意,此函数长35行。虽然理解起来也不是非常难,但如果我们应用“小”原则来重构辅助函数,则会大大简化。更新和改进后的版本如下。

function buildUrl(url, options) { const baseUrl = _getBaseUrl(url); const opts = _getOptions(url, options);
 if (!opts) { return baseUrl; }
 urlWithPath = _AppendPath(baseUrl, opts.path); urlWithPathAndQueryParams = _appendQueryParams(urlWithPath, opts.queryParams) urlWithPathQueryParamsAndHash = _appendHash(urlWithPathAndQueryParams, opts.hash);
 return urlWithPathQueryParamsAndHash;};
function _getBaseUrl(url) { if (url ===  || typeof(url) === 'object') { return ''; } return url;}
function _getOptions(url, options) { if (typeof(url) === 'object') { return url; } return options;}
function _appendPath(baseUrl, path) { if (!path) { return baseUrl; } return baseUrl += '/' + path;}
function _appendQueryParams(urlWithPath, queryParams) { if (!queryParams) { return urlWithPath }
 const keyValueStrings = Object.keys(queryParams).map(key => { return `${key}=${queryParams[key]}`; }); const joinedKeyValueStrings = keyValueStrings.join('&');
 return `${urlWithPath}?${joinedKeyValueStrings}`;}
function _appendHash(urlWithPathAndQueryParams, hash) { if (!hash) { return urlWithPathAndQueryParams; } return `${urlWithPathAndQueryParams}#${hash}`;}

你会注意到,虽然我们没有严格遵守每个函数4行的原则,但我们创建了几个相对“较小”的函数。每个函数仅完成一项任务,你可以根据函数名轻松理解这段代码。如果需要,你甚至可以针对每个函数进行单元测试,而不是只测试一个大型的buildUrl函数。你可能还会注意到,这种方法产生的代码略多一些,从35行变成了55行。这完全可以接受,因为这55行代码比原来的35行更加方便维护和阅读。

如何才能编写出这样的代码?我个人认为,最简单的方法是列出你希望逐步完成的各项任务。每一步都可以是建立某个子函数或辅助函数。例如,针对上述buildUrl函数我们希望完成如下工作:

  1. 初始化baseUrl和options

  2. 添加路径(如果有的话)

  3. 添加查询参数(如果有的话)

  4. 添加锚点(如果有的话)

请注意,上述每一步都可以直接转化为子函数。一旦养成了这样的习惯,你就可以使用这种自顶向下的方法编写所有代码,然后根据上述步骤列表,建立子函数,再针对每个子函数继续递归,创建步骤列表、建立子函数,以此类推。

下面再来谈谈单一功能原则。根据维基百科,单一功能原则的定义如下:

在面向对象编程领域中,单一功能原则(Single Responsibility Principle)规定每个类都应该有一个单一的功能,并且该功能应该由这个类完全封装起来。所有它的(这个类的)服务都应该严密的和该功能平行(功能平行,意味着没有依赖)。

在《代码整洁之道》中,Robert Martin给出了另一个定义:

单一功能原则表明,类或模块应有且只有一条加以修改的理由。

假设我们正在建立一个需要某种报告以及显示报告的系统。比较朴素的做法是构建一个存储报告数据以及用于显示报告的逻辑模块/类。但是,这违反了单一功能原则,因为修改该类的高层原因出现了两个。首先,如果报告字段发生变化,我们需要修改类;其次,如果报表可视化要求发生变化,我们也需要修改类。因此,我们不提倡利用一个类存储数据和显示数据的做法,我们应该将这些概念和所有权区域划分为两个不同的类,例如ReportData和ReportDataRenderer等。

别纠结,提高代码整洁度也没那么难

 

函数不能有副作用

副作用确实是罪恶之源,因为副作用的存在,编写没有错误的代码会非常困难。看看下面的例子,你能看出副作用吗?

functiongetUserByEmailAndPassword(email, password) {  let user = UserService.getByEmailAndPassword(email, password); if (user) { LoginService.loginUser(user); //Log user in, add cookie (Side effect!!!!) } return user;}

根据函数名所示,这个函数的目的是通过电子邮件/密码组合查找用户,这是所有Web应用程序的标准操作。然而,如果你没有阅读代码的实现,就不知道这个函数还有一个隐藏的副作用:在用户登录时,创建一个登录令牌,将其添加到数据库中,然后将cookie发送给用户,而用户则“成功登录”。

这中间有很多问题。

首先,不阅读实现代码就不知道该函数的功能/接口。即使你通过文档说明该函数登录的副作用,也仍然不是理想的做法。工程师喜欢使用现代IDE中的智能提示,因此当遇到一个简单的函数名时,大部分人都不会阅读相应的文档。他们会利用这个函数来获取用户对象,却没有意识到他们正在请求中添加Cookie,这可能会引发很多棘手且不易发现的bug。

其次,考虑到所有的依赖关系,测试这个函数相当困难。你需要验证是否可以通过电子邮件/密码顺利找到用户,需要模拟HTTP响应以及登录令牌的写入。

第三,用户查找和登录之间的紧密结合必然无法满足将来的所有用例,例如,你可能需要单独查找用户或登录用户。换句话说,这个函数不具有前瞻性。

别纠结,提高代码整洁度也没那么难

 

总结

总的来说,你需要牢记以下四个提高代码整洁度的原则,并通过这些原则提高团队的生产力:

  1. 没有经过测试的代码一概不安全

  2. 选择有意义的名称

  3. 类与函数保持最小,遵守单一功能原则

  4. 函数不能有副作用

感谢您的阅读!

原文:https://engineering.videoblocks.com/these-four-clean-code-tips-will-dramatically-improve-your-engineering-teams-productivity-b5bd121dd150



Tags:代码   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
前言几乎所有.NET序列化程序的实现基础都是反射。下列代码是Newtonsoft.Json的实现:protectedvirtualJsonPropertyCreateProperty(MemberInfomember,MemberSerializationmemb...【详细内容】
2021-12-28  Tags: 代码  点击:(2)  评论:(0)  加入收藏
在SEO优化中,最重要的评估之一就是确定网站上存在哪些HTTP状态代码。这些代码可能会变得很复杂,成为一个难题,必须先解决这些难题,然后才能完成其他任务。例如,如果你放置的页面...【详细内容】
2021-12-24  Tags: 代码  点击:(5)  评论:(0)  加入收藏
1、通过条件判断给变量赋值布尔值的正确姿势// badif (a === 'a') { b = true} else { b = false}// goodb = a === 'a'2、在if中判断数组长度不为零...【详细内容】
2021-12-24  Tags: 代码  点击:(6)  评论:(0)  加入收藏
前言本文提供将视频调整分辨率的Python代码,一如既往的实用主义。环境依赖ffmpeg环境安装,可以参考我的另一篇文章: windows ffmpeg安装部署_阿良的博客-CSDN博客ffmpy安装:pip...【详细内容】
2021-12-14  Tags: 代码  点击:(15)  评论:(0)  加入收藏
大家好, 我是林路,今天就给大家介绍Python代码都是用的什么编辑器写的?Jupyter Notebook ,没有Pycharm,没有Vscode,没有Sublime text。 只有一款工具:Jupyter Notebook 。工欲善其...【详细内容】
2021-12-09  Tags: 代码  点击:(27)  评论:(0)  加入收藏
在这篇文章中,我将与你分享一些关于JS的技巧,可以提高你的JS技能。1.避免if过长如果判断值满足多个条件,我们可能会这么写:if (value === 'a' || value === 'b'...【详细内容】
2021-11-17  Tags: 代码  点击:(22)  评论:(0)  加入收藏
一、为什么需要使用内存池在C/C++中我们通常使用malloc,free或new,delete来动态分配内存。一方面,因为这些函数涉及到了系统调用,所以频繁的调用必然会导致程序性能的损耗;另一...【详细内容】
2021-11-17  Tags: 代码  点击:(38)  评论:(0)  加入收藏
《开源精选》是我们分享Github、Gitee等开源社区中优质项目的栏目,包括技术、学习、实用与各种有趣的内容。本期推荐的是一个由百度开源的低代码前端框架——amis...【详细内容】
2021-11-05  Tags: 代码  点击:(68)  评论:(0)  加入收藏
程序员是青春饭,这在国内似乎是公认的。所以很多公司不愿招大龄程序员,很多程序员也“知趣”地及早转型。有的做管理,有的做架构,我还见过改行卖保险的。总之,年龄大了不想敲代码...【详细内容】
2021-10-27  Tags: 代码  点击:(30)  评论:(0)  加入收藏
我们来看看我们拨号键盘除了能打电话还能干什么iphone 的拨号键盘除了用来拨号,其实暗藏代码输入星井06井可以查询手机真实的IMEI码,这个码是独一无二的没有双胞胎 输入星3001...【详细内容】
2021-10-25  Tags: 代码  点击:(78)  评论:(0)  加入收藏
▌简易百科推荐
本文分为三个等级自顶向下地分析了glibc中内存分配与回收的过程。本文不过度关注细节,因此只是分别从arena层次、bin层次、chunk层次进行图解,而不涉及有关指针的具体操作。前...【详细内容】
2021-12-28  linux技术栈    Tags:glibc   点击:(3)  评论:(0)  加入收藏
摘 要 (OF作品展示)OF之前介绍了用python实现数据可视化、数据分析及一些小项目,但基本都是后端的知识。想要做一个好看的可视化大屏,我们还要学一些前端的知识(vue),网上有很多比...【详细内容】
2021-12-27  项目与数据管理    Tags:Vue   点击:(2)  评论:(0)  加入收藏
程序是如何被执行的  程序是如何被执行的?许多开发者可能也没法回答这个问题,大多数人更注重的是如何编写程序,却不会太注意编写好的程序是如何被运行,这并不是一个好...【详细内容】
2021-12-23  IT学习日记    Tags:程序   点击:(9)  评论:(0)  加入收藏
阅读收获✔️1. 了解单点登录实现原理✔️2. 掌握快速使用xxl-sso接入单点登录功能一、早期的多系统登录解决方案 单系统登录解决方案的核心是cookie,cookie携带会话id在浏览器...【详细内容】
2021-12-23  程序yuan    Tags:单点登录(   点击:(8)  评论:(0)  加入收藏
下载Eclipse RCP IDE如果你电脑上还没有安装Eclipse,那么请到这里下载对应版本的软件进行安装。具体的安装步骤就不在这赘述了。创建第一个标准Eclipse RCP应用(总共分为六步)1...【详细内容】
2021-12-22  阿福ChrisYuan    Tags:RCP应用   点击:(7)  评论:(0)  加入收藏
今天想简单聊一聊 Token 的 Value Capture,就是币的价值问题。首先说明啊,这个话题包含的内容非常之光,Token 的经济学设计也可以包含诸多问题,所以几乎不可能把这个问题说的清...【详细内容】
2021-12-21  唐少华TSH    Tags:Token   点击:(10)  评论:(0)  加入收藏
实现效果:假如有10条数据,分组展示,默认在当前页面展示4个,点击换一批,从第5个开始继续展示,到最后一组,再重新返回到第一组 data() { return { qList: [], //处理后...【详细内容】
2021-12-17  Mason程    Tags:VUE   点击:(14)  评论:(0)  加入收藏
什么是性能调优?(what) 为什么需要性能调优?(why) 什么时候需要性能调优?(when) 什么地方需要性能调优?(where) 什么时候来进行性能调优?(who) 怎么样进行性能调优?(How) 硬件配...【详细内容】
2021-12-16  软件测试小p    Tags:性能调优   点击:(20)  评论:(0)  加入收藏
Tasker 是一款适用于 Android 设备的高级自动化应用,它可以通过脚本让重复性的操作自动运行,提高效率。 不知道从哪里听说的抖音 app 会导致 OLED 屏幕烧屏。于是就现学现卖,自...【详细内容】
2021-12-15  ITBang    Tags:抖音防烧屏   点击:(25)  评论:(0)  加入收藏
11 月 23 日,Rust Moderation Team(审核团队)在 GitHub 上发布了辞职公告,即刻生效。根据公告,审核团队集体辞职是为了抗议 Rust 核心团队(Core team)在执行社区行为准则和标准上...【详细内容】
2021-12-15  InfoQ    Tags:Rust   点击:(25)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条