前语

提高代码质量是程序员永久的论题。数不清的文章书本珠玉在前:《代码整齐之道》、《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有一大堆准则和实例,这儿就不逐个列出赘述了,谈谈觉得重要的两点。

①提炼函数 一个函数应该只干一件事情,是下载就不要搞解压,是获取就不要搞处理。

日常代码编写中,要留意对长函数进行提炼,使其尽可能变成变成一个个自带解说的细粒度函数的组合/联接。

我以为的自带解说的细粒度函数:

  1. 只干一件事
  2. 尽量纯,只依靠入参,不依靠外部状况、变量,不在函数内部修正外部全局变量。
  3. 假如真的做到了以上两点,再配上一个有明晰语义的姓名,那么看这些链接起来的函数,就会像是在看注释,如水银泄地。

假如要举个,那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)规则每个类都应该有一个单一的功用,而且该功用应该由这个类彻底封装起来。所有它的(这个类的)服务都应该紧密的和该功用平行(功用平行,意味着没有依靠)。

界说很简略,但日常开发中很简略碰到以下几种状况:

  1. 一个类行数过多
  2. 一个类各种依靠
    依靠多,相互影响简略改坏,也无法写单元测试。
  3. 一个类什么操作都有。
    是xxxManager却在内部界说各种界说工具办法,甚至搞成static为外部调用。是xxxUtils却还持有着不断改变的成员变量。这就导致,类的命名和实践责任不符,或许说很难有一个合适、简略的命名来命名这个类。

其实便是违反了我们初学语言时,教师第一节课都会讲“松耦合,高内聚”的准则。假如在日常代码开发中,发现有上面说的几种状况,就应该考虑代码优化问题。

3.合理的复用
从代码维护的角度闲话代码质量
cv真的很爽,可是出了问题还是火葬场。 有共性应该先试图把公有逻辑剥离出来复用,不要简略cv。

不然造成: 由A copy出来的了B、C… 有一天发现A其实有bug,但B、C却很简略被遗忘.. 改不全、改不完。

4.简练的注释

有一个古老的段子是

程序员最厌烦的两件事:1.自己写注释2.他人不写注释

前面提过,合理的命名+细粒度的代码自身自带解说。但也不是所有场景都cover的住。 :

  1. 解说型注释
    略过。
  2. 技能型注释
    简略介绍技能完成的核心思维、原理。
  3. 防坑型注释
    光靠代码没人能解说的清为什么这么写,所以写一些话粘上其时的team、链接。包括但不限于:
 1.之所以这么写是由于... 产品要求这儿要...
 2.这儿会导致.. 但.. 因此..
 3.!!!这儿不要动!!!
 4.除非你知道你在干什么,不然不要修正这儿的代码。 

短短的几句话,是前人用血和泪凝练而成的经验教训…

  1. 祈求型注释
 佛祖保佑,永无bug

诚然注释能辅佐阅览者理解代码,可是冗余的注释也不会受到欢迎。一段代码假如需要写大量的注释,那么应该先检查代码是否有问题。

5.精炼的commit

前面说到的是代码编写的问题,代码提交也有一些需要留意的当地。这儿也简略聊聊。

  1. 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的内容,就不好束缚。

  1. 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 天,点击检查活动概况