「强烈推荐」这是我看过最接“地气”的代码问题与重构实践

IT实战联盟 2020-10-22 11:15:07 ⋅ 1421 阅读

 

写这个文章是因为前段时间确实因为公司的业务开发太忙太紧,所有开发都处在于加班赶项目,并且加入的新人较多造成了一系列代码不可控的质量问题。

文章针对这段时间代码出现的各种各样的问题进行了一个概况和整理,主要集中在代码编码的问题,抽象化的问题,还有就是涉及到微服务中调用和编写接口的问题。

其实按道理来说,这些应该属于编程的基本功,貌似不太值得写一篇文章,不过倒是可以通过这些基本功的出发,去讨论一个代码编程系统构建的一个本质,所以还是比较值得去展开。

大概先铺垫下,会按照一个原则和建议来展开一个一个的进行讨论。

编码的问题

避免过多的 IF 嵌套

所谓的“箭头形”代码基本都是因为大量的 IF 嵌套导致,一方面形成一个深深的箭头形状,在阅读代码造成缩进夸张的语句块。

更要命的是过深的嵌套层次导致代码逻辑复杂度加深,当阅读到第 N 层嵌套时根本不清楚是什么逻辑才能进入,严重降低代码的可阅读性和可维护性。

「强烈推荐」这是我看过最接“地气”的代码问题与重构实践

 

其实对应 IF-ELSE 过长的主要原因无非就是对当前状态进行检查并决定继续还是跳转。

①使用卫语句(Guard Clauses)提前返回,避免层层嵌套

先对 IF/ELSE 的逻辑结构进行一些分析,我们基本上有两种用法:

"优先考虑满足条件,进行处理流程",代码如下:

    if(user.getId() == 10){
        //满足条件,执行
    }else{
        //不满足条件,退出
    }

“优先考虑不满足条件,让其逻辑退出流程”,代码如下:

    if(user.getId() != 10){
        //不满足条件,退出
    }else{
        //满足条件,执行
    }

这是两个不同的逻辑结构,他们都可以写出同样的代码逻辑,但是在第一种中,如果代码量增大,嵌套增多,就很容易在条件中迷失了方向。

如果采用第二种方式把条件反过来写,尽早的能把退出型逻辑及早的退出,这样就可以把箭头型的代码解脱掉,如下图:

「强烈推荐」这是我看过最接“地气”的代码问题与重构实践

 

②规划好判断条件和状态模型

代码如下图:

「强烈推荐」这是我看过最接“地气”的代码问题与重构实践

 

如果是业务允许其实是可以将多个判断条件进行整合,这样可以避免箭头形代码的出现。

但是仅仅一段 IF 条件判断的语句又变得非常的臃肿一行都放不下,如果出现了非常复杂多状态判断和组合,可以使用“状态表”,或者是状态机等设计模式来进行解耦。

③将 IF 中的业务细节进行抽象成函数

将 IF 中繁琐的业务细节抽成函数,一方面可以减少又长又臭的代码,更利于屏蔽细节,将不关流程的业务逻辑锁定在一个特定的区域。

也利于进行代码阅读,让阅读关注于业务的流程而不是业务实现的细节,要善于应用函数用于代码的封装和抽象。

「强烈推荐」这是我看过最接“地气”的代码问题与重构实践

 

谨慎多层循环嵌套中的操作

有的时候,确实几层 for 循环的嵌套是业务实现的必须,但我们需要警惕的是经过几层循环的放大,最内层循环执行的数量是多层循环数量的乘积。

「强烈推荐」这是我看过最接“地气”的代码问题与重构实践

 

例如,这段代码总共经历了 4 层的循环,如果循环是 10x10x10x10,那么最终的 DB 操作是要经历单独的开销 10000 次。

第一,这 10000 次开销如果是程序员在写代码已经明确知道的开销属于业务必须那倒无妨,只怕程序员在写代码的时候还无意识到这个点是会被随时放大。

第二,即使 10000 次开销是属于业务必须,那按照这个代码来看,还是存在可以优化的空间,可以在循环中将所有查询条件都进行拼凑,然后在进行一定程度的批量查询,可以较大程度降低 DB 的开销。

不要随意定义局部变量名

命名风格我们可以参考阿里的《Java开发手册》,这里主要指出来的是局部变量随意命名的现象比较严重,大家一般都会以为局部变量只是在本方法内使用,又不会对其他方法和其他人造成影响。

但殊不知局部变量名起得不好或随意也对开发者本身造成困扰甚至连自己到不知道的错误,以下是一个比较经典的随意起变量名的例子:

「强烈推荐」这是我看过最接“地气”的代码问题与重构实践

 

变量名 ma 和 map 没有本身含义,并且他们的泛类又是一样,很难保证不会再下面的代码不小心使用错误。

避免又臭又长的类和方法

一点都不夸张,之前看到过一类一千多行,一个方法长达 300 行,IDE 大概一页正常来说 30-50 行(取决屏幕大小),这个叫阅读者怎么查看。

阅读的时候,不断的滚轮翻页,就算是原作者,恐怕时间一长也很难驾驭这个类,就不用说后来的维护者了。

更重要的是一个类,一个方法过长时,会严重阻碍你的扩展和修改,方法中每一个逻辑都牵扯到很多分散的上下文,会让修改和扩展异常困难。

按照《重构》所说,出现类过长的情况很多是职责不明确,一个类存在着几十个方法,那绝对是职责过多或职责不细分。

简单列一下针对又长又臭的重构处理:

  • 分析需要重构类的功能。
  • 将职责相同的方法使用组合或集成的方式抽取为独立的类。
  • 分析各个方法,将重复的代码提取为函数。
  • 命名,对类有一个好的命名有利于对类的定位和确立职责。

Log 日志要提供明确的指向,辅助定位

Log 日志要有明确的指向性,一个可以辅助调试,一个可以记录事件,和确立定位错误。

像以下的这个例子,打印了一个 log.error 日志,但这个错误,就算我们事后去查看日志,只知道这里有一个错误日志,但究竟是哪一个用户日志,哪一张优惠券的日志,无从得知,不能有助于我们直接定位错误。

「强烈推荐」这是我看过最接“地气”的代码问题与重构实践

 

再看一下的日志,将返回的一个 List 进行直接打印,此处的打印并无助于保留和定位问题,只会留下无价值的信息并且让日志变得乱糟糟。

通常,我们留下实体名字和逻辑关键字就足以识别一条记录。

「强烈推荐」这是我看过最接“地气”的代码问题与重构实践

 

复杂模块,代码未动,大纲注释先行

要阻止一个初级的程序员一上来就写代码的难度堪比阻止一饥饿的人要饱餐一顿,有多少程序员被称之为码农,一上来就想搬砖。

在流程和系统的设计上,我们有 E-R 图和流程图,帮我们建立模型和流程。

当我们碰到逻辑比较复杂的类或方法,我们也需要先梳理好逻辑和流程,用注释或伪代码定好逻辑和流程,把整体的思路确立后,搭起一个骨架,再往里面填肉(写代码)。

只要流程清晰,逻辑明朗,这个时候写代码其实是最简单的事情。

「强烈推荐」这是我看过最接“地气”的代码问题与重构实践

 

功能相同尽量抽象,不要发散式修改

举这次我们构建订单的一个例子,见下图:

「强烈推荐」这是我看过最接“地气”的代码问题与重构实践

 

下单在后端使用了适配者的一个设计模式,主要是包装同一个接口对外暴露,然后根据情况(商品的逻辑)进行实现类的分离。

把逻辑统一并包装成统一接口对外暴露这个本意是良好的,但是在这里例子中,只在意了商品逻辑的分离,而忽略了,其实逻辑,例如库存,支付,优惠券等逻辑其实是统一的,是可以被抽象的。

导致的结果是例如需要修改优惠券逻辑式,需要同时进行三次几乎一模一样的修改。

可以从上图看出来,过早的使用适配模式,将业务在入口处进行分离,导致了后续其实相同逻辑的业务代码也进行了分离,本来 “扣库存” “扣优惠券” “支付”等逻辑应该是一样,但也使用了三套代码进行维护。

微服务编码问题

RPC 接口必须是业务职责

RPC 接口是微服务的生产者提供一定的能力给到消费者进行使用,这个时候的 RPC 接口千万不要定义大而全的接口。

之前就发现有部分同学把 RPC 接口定义成:

insertXXX 

updateXXX 

listXXX

这样无异于把 DAO 层直接搬到了 RPC,把整个 DAO 直接进行暴露,这样违背了微服务的接口调用原则,RPC 接口只提供最原子的功能,限制消费者在生产者定义好的业务中进行使用。

严禁循环调用 RPC 接口

与项目内编程不同的是,每个 RPC 接口的调用都会伴随着一次的网络开销,需要需要对一个接口进行反复请求,这个时候可以要求 RPC 接口的提供方另外提供一个可以批量的接口,将单次反复的请求变成一次请求,减少网络开销。

「强烈推荐」这是我看过最接“地气”的代码问题与重构实践

 

使用工具辅助清理恶性代码

P3C 插件

在使用 Eclipse 或 idea 编程中,首推使用阿里的 P3C 插件进行辅助,代码规范检查插件 P3C,是根据《阿里巴巴Java开发手册》转化而成的自动化插件。

「强烈推荐」这是我看过最接“地气”的代码问题与重构实践

 

使用 Skywalking 找出恶性代码


与 P3C 直接辅助编码不同的是,Skywalking可以在生产环境中通过链路的跟踪确定某一个微服务的接口性能或调动出现异常。

这里不累赘介绍 Skywalking 的用处,其实链路跟踪不仅仅是运维或架构师应该关注的点,普通的开发者也可以借助链路跟踪去回溯自己的代码,站在一个高的角度在生产环境中审视代码在链路中表现。

善于使用链路跟踪往往可以发现在平时编码中被忽略的问题,例如,一次不经意的循环调用 RPC 很容易就造成超大的调用跨度,而往往在编程中开发者是未能及时感知的。

「强烈推荐」这是我看过最接“地气”的代码问题与重构实践

 

小结

在分享的时候其实还讲了抽象的原则和一些设计模式的使用,这里就不累赘的复述了。

简单的说,要写出好的性能,可读性高,逻辑明了的代码,往往靠的不是一次一次的 CURD,而是平时的总结和思考。


作者:陈于喆

简介:十余年的开发和架构经验,国内较早一批微服务开发实施者。曾任职国内互联网公司网易和唯品会高级研发工程师,后在创业公司担任技术总监/架构师。


全部评论: 0

    我有话说:

    搞对数据库连接池,次从100优化到3ms!阿里架构师都说好

    在研究HikariCP(一个数据库连接池)时无意间在HikariCPGithub wiki上看到了一篇文章(即前面给出),篇文章有力消除了一直以来疑虑,完之后感觉神清气爽。故在此

    Flink + 强化学习搭建实时推荐系统

    中有两个值得关注地方: 可被视为一个推荐...

    精品推荐:【CKEditor】全球优秀网页在线文字编辑器之一

    CKEditor新一代FCKeditor,一个重新开发版本。CKEditor全球优秀网页在线文字编辑器之一,因其惊人性能可扩展性而广泛被运用于各大网站。

    精品推荐:无推送,无新闻,无广告,2倍速视频,直播超强浏览器

    今天给大家推荐一款小众却功能强大应用,款应用不怎么出名,一直很低调,但是却受到众多好评,相对其他大众

    SourceTree里GitFlow使用

    几天详细了一下Git Flow模型介绍,感觉“很好很强大”,这个开发模型利用GIT易于分支和合并特点,能够比较容易将开发、发布、部署、bug修复分隔开来。 正准备在自己团队内部推广

    世界之开发项目:苦撑12年,600多万行代码

    “ 你见项目,撑了多长时间才完蛋?六个月?一年?今天介绍这个奇葩项目,不但一开始就烂得透透,还硬...

    10个“优秀”代码注释

    致终于来到这里勇敢人。神奇。勿动。

    精品推荐篇就全懂负载均衡了

    随着互联网发展,业务流量越来越大并且业务逻辑也越来越复杂,单台机器性能问题以及单点问题凸显了出来,因此需要多台机器来进行性能水平扩展以及避免单点故障。

    推荐一款功能强大,开源免费H5可视化编辑器

    H5-Dooring 一款功能强大,开源免费H5可视化页面配置解决方案,致力于提供一套简单方便、专业可靠、无限可能H5落页最佳实践。技术栈以react为主, 后台采用nodejs开发. 预览

    2021 年开发语言会谁?

    哪种语言会比较火🔥? 开发语言走势一直都在牵动程序员心。 2020 年已经过半,时候分析下明年趋势了。 下面咱们就下权威行业数据,看看 2021 年可能排在前 3 开发语言都有谁

    上篇:前端必8个HTML+CSS技巧(下)

    4款非常容易上手酷炫效果,值得收藏。

    Google技术:你不了解Google最新发布JS代码规范 最佳实践

    Google为了那些还不熟悉代码规范人发布了一个JS代码规范。其中列出了编写简洁易懂代码所应该做最佳实践

    好文推荐如何用redis做实时订阅推送

    目前项目已上前线,运行平稳~~~

    DDD 微服务落实战

    PS:让我们把DDD思想真正落地,从数据库设计到代码实战演练,从电商、在线订餐 到智慧医疗全方位展示如何运用DDD 在微服务+人工智能、嵌入式+物联网项目中进行业务建模、系统规划设计实践

    你想更好理解Node.js中Buffer吗?一下个。

    不论是否科班出身,认真读完,想必会给你带去一些收获.

    【开源推荐】基于 Go 语言轻量级高性能日志库 logit使用及测评

    logit 一个简单易用并且基于级别控制日志库,可以应用于所有 GoLang 应用程序中。

    Redis为什么变慢了?Redis性能问题排查详述

    Redis 作为优秀内存数据库,其拥有非常高性能,单个实例 OPS 能够达到 10W 左右。但也正因此如此,当我们在使用 Redis 时,如果发现操作延迟变大情况,就会我们预期不符。 你