前语
提高代码质量是程序员永久的论题。数不清的文章书本珠玉在前:《代码整齐之道》、《Effective Java》等各种指南,辅导着程序员们前赴后继,奔向功率恐怖、代码风流的彼岸….
除了翻阅书本检查资料,从在学校为所欲为敲代码到在公司编写符合标准的代码并上线,自己感触最深的敏捷提高代码质量有两个方式:
①CodeReview->被喷->修正->今后不敢这么写了
②保护前人老代码,排查一些八怪七喇的问题->改不出来->改不出来(循环n次)->改出来了->回忆问题出在哪
本文就从代码保护的视点,从实例出发,说通俗易懂的话(发肆无忌惮的表情包),轻松闲聊下代码质量的那些事儿…
正文
我看不懂,但我大受震慑
保护中最简略产生的疑问是:看不懂,这个函数在干啥啊?这个值是啥啊?为什么要这么写?这真的不是个bug吗?….
reedit论坛上有过一个很的被我们戏称为“程序员酒后真言”的帖子里有这么一则:
Good code is code that can be understood by a junior engineer. Great code can be understood by a first year CS freshman. The best code is no code at all.
这是一个很有趣的论调,甚至最终这句有点儿“大音希声,大象无形”的神韵了。不管它是否彻底正确,不可否认的是,代码的可读性的确很重要。代码只要便于理解,才有可能被除了作者以外的人保护。
1.明晰的语义
一个类、办法、变量等都应该有明晰的语义,达到的作用应该是: 搂一眼姓名,至少大致明白它是干啥的。
①不要在代码里运用语义不明的硬编码
甚至像ui尺寸这种数据,假如在代码里多处用到,也请界说成常量
尽管这些值仅仅经验值/脑袋一拍得出来的
举个:
private static final int TITLE_TEXT_SIZE_SP = 18;
private static final int MAX_VIDEO_DURATION_MILLISECONDS = 24 * 1000;
private static final int STYLE_IMG_WIDTH_PX = CommonUtil.dip2px(42);
而且在常量界说的后缀上明晰单位,这样做的优点是,需要修正时能够防止遗失,而且常量命名自带解说。
②假如能够更具象,就不要用handle、process这类抽象化的表达
相似handleEvent()、processTask() 甚至于 doSomeThing() 这种抽象化的表达尽量不要运用。
一旦行数较多,理解成本变高,阅览者就不得不完整阅览才能知道终究做了什么处理。很简略打断思路,使人不得不点进去,不然不知道会发生些什么,会不会对下文有影响,这就会影响代码阅览与保护的功率。
细粒度的函数应该是自解说的,这一点后面会说到。
2.明晰的结构
臃肿的类/过长的函数总是被会集吐槽的对象,臃肿的类/过长的函数意味着难以阅览、难以寻觅要害逻辑、难以保护。
相关改善的办法google有一大堆准则和实例,这儿就不逐个列出赘述了,谈谈觉得重要的两点。
①提炼函数 一个函数应该只干一件事情,是下载就不要搞解压,是获取就不要搞处理。
日常代码编写中,要留意对长函数进行提炼,使其尽可能变成变成一个个自带解说的细粒度函数的组合/联接。
我以为的自带解说的细粒度函数:
- 只干一件事
- 尽量纯,只依靠入参,不依靠外部状况、变量,不在函数内部修正外部全局变量。
- 假如真的做到了以上两点,再配上一个有明晰语义的姓名,那么看这些链接起来的函数,就会像是在看注释,如水银泄地。
假如要举个,那rxjava的流式处理便是最常见的:
xxxApiService.getXXX()//网络恳求
.map(new ResponseFunction<>());//数据解包
.filter(xxxCondition)//数据过滤
.flatMap(xxx1)//事务逻辑,处理1
.flatMap(xxx2)//事务逻辑,处理2...
.subscribe({},{})//UI展现
这就挺符合上面说到的思维,从网络恳求->数据处理->事务逻辑->UI展现环环相扣,真出了bug,也能很快知道在哪个环节去找问题。
而相反的,程序员一定见过相似的代码(伪):
funcXXX{
if{
读取全局变量a,xxx
if{
写入全局变量b,创建局部变量c,xxx
if{
跑一个任务,xxx,在回调里修正xxxxx,
if{
修正全局变量a,操作一下ui,xxx
if{
再干点别的
}
}
}
}
}
}
现编了一点儿伪代码,可是在事务中的确挺简略碰到。
嵌套深,操作多,简略不敢改,上下文影响大。保护起来就非常头疼。
②单一责任 上面说到了臃肿的类/过长函数让人头疼,以及函数的提炼/细粒度化,紧接我们就聊下类/模块的单一责任问题。
单一责任的维基百科界说:
在[面向对象编程]领域中,单一责任准则(Single responsibility principle)规则每个类都应该有一个单一的功用,而且该功用应该由这个类彻底封装起来。所有它的(这个类的)服务都应该紧密的和该功用平行(功用平行,意味着没有依靠)。
界说很简略,但日常开发中很简略碰到以下几种状况:
- 一个类行数过多
- 一个类各种依靠
依靠多,相互影响简略改坏,也无法写单元测试。 - 一个类什么操作都有。
是xxxManager却在内部界说各种界说工具办法,甚至搞成static为外部调用。是xxxUtils却还持有着不断改变的成员变量。这就导致,类的命名和实践责任不符,或许说很难有一个合适、简略的命名来命名这个类。
其实便是违反了我们初学语言时,教师第一节课都会讲“松耦合,高内聚”的准则。假如在日常代码开发中,发现有上面说的几种状况,就应该考虑代码优化问题。
3.合理的复用
不然造成: 由A copy出来的了B、C… 有一天发现A其实有bug,但B、C却很简略被遗忘.. 改不全、改不完。
4.简练的注释
有一个古老的段子是
程序员最厌烦的两件事:1.自己写注释2.他人不写注释
前面提过,合理的命名+细粒度的代码自身自带解说。但也不是所有场景都cover的住。 :
-
解说型注释
略过。 -
技能型注释
简略介绍技能完成的核心思维、原理。 -
防坑型注释
光靠代码没人能解说的清为什么这么写,所以写一些话粘上其时的team、链接。包括但不限于:
1.之所以这么写是由于... 产品要求这儿要...
2.这儿会导致.. 但.. 因此..
3.!!!这儿不要动!!!
4.除非你知道你在干什么,不然不要修正这儿的代码。
短短的几句话,是前人用血和泪凝练而成的经验教训…
- 祈求型注释
佛祖保佑,永无bug
诚然注释能辅佐阅览者理解代码,可是冗余的注释也不会受到欢迎。一段代码假如需要写大量的注释,那么应该先检查代码是否有问题。
5.精炼的commit
前面说到的是代码编写的问题,代码提交也有一些需要留意的当地。这儿也简略聊聊。
-
commit msg :
作业时commit msg没有强制要求,见过不少msg比如”一些修正/几个bug“这款式儿的,就很利诱。
-
一句话精炼描绘 + 代码提交类型(feature/bug/refator) + 文档链接 + 修正布景 + 修正思路 + 影响规模 +xxx其他比如版本号之类。
-
防止重复:刚参加作业时就犯过相似的过错,几个commit的msg都共同,被大佬提醒后就从速改了(逃)。
假如几个提交都是为了处理同一个问题,且的确不存在分开提交的必要,可运用rebase -i 合并commit,或许干脆reset重提吧。
commit msg很重要,由于
-
cr同学在review前先看一眼commits的msg,就能大致了解整体代码编写的思路是否连接。
-
保护的同学在阅览代码时,检查/过滤commit msg也能了解其时代码编写的布景。
-
幻想一个场景,你碰到了一个棘手的问题很难处理,不明白为啥这儿代码要这么写但又觉得一定是有特殊的原因,翻开commit msg发现是”一些修正/几个bug”这款式儿的。想找最初提交的同学问问,又发现现已查无此人。此刻还有勇气改代码吗/手动狗头
不过说起来,commit msg/Merge request的编写团队协作都可配置模板,其实完形填空就行。
可是每个commit的内容,就不好束缚。
- commit 内容
- 限制一次commit的代码行数,有意识的拆分commit!!!
一个commit提交上千行代码,这些代码彻底干了不止一件事儿(建立根本结构、修正ui、恳求网络…),相当于把一次feature的代码全糅在一个commit里去做。review同学会很苦楚,时刻久了之后自己也无法了解其时代码的编写思路。出了错,定位也麻烦。这儿就不多说,引荐下看到过的一篇文章。
怎么写好提交,做一个有品味的开发者 help.aliyun.com/document_de…
谁能告诉我终究发生了什么?
前面主要从保护的视点闲聊了下代码的可读性问题,但一份代码要简略保护,可读性强还不行。
没有日志,没人能知道用户终究运用了什么样的上古神机,以什么样古怪的姿态在运用app。
1.用日志debug
要养成一个习气,程序出了问题,先看日志,日志处理不了,再打断点。断点打完后,弥补缺失的日志。
由于改bug便是在排查哪里状况出了反常,程序是状况的合集,而日志担任打印这些状况。
这种习气养成今后,了解的事务排查起问题来会越来越快,上下文的日志一检索,就知道大约发生了什么,哪里有反常。
别的,假如线下debug就得有必要用断点,日志看不出来问题的话,到了线上,等问题反应回来,只能得到“先加一波日志,等后续排查”这样的成果。
但加日志也得等发版,一个由于日志缺失而无法排查的问题,一来二去时刻就会拖得很长。
2.满足的日志
入参、出参、分叉路径等,都应该有满足的日志。
一段出了问题的日志回捞上来,要能够定位到用户在什么页面,有哪些要害操作步骤,事务代码走到了哪些分叉…..
但不要污染日志,包括不限于:
- 打印各种整个的网络恳求数据
- 一些重复触发但又意义不大的ui改变,如ui滑动之类
3.日志的等级与处理
日志的等级与运用、处理应该一致。
-
i、d、w、e 过错等级要和日志等级匹配。
不要由于日志比较重要想要提醒自己,就运用不匹配的日志等级,然后打印一堆日志error,实践程序正常运转不受影响。无用的error会掩盖真实严峻的影响用户体会的过错。 -
程序状况反常的严峻问题除了要loge以外,应该上报过错,不然会呈现意想不到的大大大大坑。
例如用户进不了页面,但又没溃散,也没有独自过错上报上来,就只能等用户反应。等反应到手里,大锅就得背上身。
真实的精华与总结!
代码质量是永久的论题,本文主要从代码保护视点闲聊了代码质量关于可读性、日志编写的问题,也只涉及到了冰山一角。
由于一时兴起,而不断码出这篇文章的过程中,一些疑问从魂灵中爆发:
-
我在这儿巴拉巴拉这么多,我的代码质量高吗,能够确保不犯文中的那些过错吗,不成为结构山的一员吗?
-
那些根深蒂固、难以保护的事务代码,真的都是由于程序员不重视代码质量,乱写一气吗?
我信任绝大部分程序员谈到代码质量都能侃侃而谈,聊上那么几句。也信任有追求的程序员都会留意代码质量问题。
那为啥代码的山越垒越高?”抽象”的代码屡次呈现?
是人性的扭曲,还是道德的沦丧?
不!很多时候都是由于事务太复杂,而时刻太紧迫!
小王,这个功用需要这么久吗? 这个不是很简略吗? 不便是….
终究什么时候能够上线?老板发话了,有必要赶上这个版本!
为什么这么简略的一个需求需要这么排期这么久?
需求做到一半,产品/规划:尽管其时是这么规划的(规划了一辆二轮自行车),可是后来觉得…,所以我们现在要改成xxxxx(改成一辆插着翅膀,混合动力,背着两个轮胎,海陆空三用的小汽车)才行。
所以最终程序员的心里和写出来的代码就很简略变成:
- 能跑就行
- 不是没崩吗?
- 又不是不能用?
后话
从上学到作业,都有写博客的习气。最开始在csdn,后来在简书,可惜一个现已自暴自弃,一个又满是吸引眼球的营销文。 刚好看到有更文活动,最近有富裕的时刻来写点东西,所以敲起键盘…
记录文字让人心里平静。
开启成长之旅!这是我参加「日新计划 2 月更文挑战」的第 1 天,点击检查活动概况