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

图片来自 Pexels

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

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

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

编码的问题

避免过多的 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,而是平时的总结和思考。

作者:陈于喆

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

编辑:陶家龙

原文首发于51CTO技术栈

END -

想要加入中生代架构群的小伙伴,请添加群合伙人大白的微信

申请备注(姓名+公司+技术方向)才能通过哦!

阿里技术精彩文章推荐

往期推荐

深度:揭秘阿里巴巴的客群画像

多隆:从工程师到阿里巴巴合伙人

阿里技术专家楚衡:架构制图的工具与方法论

蚂蚁集团技术专家山丘:性能优化常见压测模型及优缺点

阿里文娱技术专家战獒: 领域驱动设计详解之What, Why, How?

阿里专家马飞翔:一文读懂架构整洁之道

阿里专家常昊:新人如何上手项目管理?

蚂蚁集团沈凋墨:Kubernetes-微内核的分布式操作系统

阿里合伙人范禹:常挂在阿里技术人嘴边的四句土话

阿里技术专家都铎:一文搞懂技术债

支付宝研究员兼OceanBase总架构师杨传辉:我在数据库梦之队的十年成长路

阿里技术专家麒烨:修炼测试基本功

阿里计算平台掌门人贾扬清:我对人工智能方向的一点浅见

蚂蚁资深算法专家周俊:从原理到落地,支付宝如何打造保护隐私的共享智能?

阿里高级技术专家箫逸:如何画好一张架构图?

阿里高级技术专家张建飞:应用架构分离业务逻辑和技术细节之道

蚂蚁科技 Service Mesh 落地实践与挑战 | GIAC 实录

阿里6年,我的技术蜕变之路!

蚂蚁集团涵畅:再启程,Service Mesh 前路虽长,尤可期许

阿里P9专家右军:大话软件质量稳定性

阿里合伙人程立:阿里15年,我撕掉了身上两个标签

阿里高工流生 | 云原生时代的 DevOps 之道

阿里高级技术专家邱小侠:微服务架构的理论基础 - 康威定律

阿里P9专家右军:以终为始的架构设计

阿里P8架构师:淘宝技术架构从1.0到4.0的架构变迁!12页PPT详解

阿里技术:如何画出一张合格的技术架构图?

蚂蚁资深技术专家王旭:开源项目是如何让这个世界更安全的?

阿里资深技术专家崮德:8 个影响我职业生涯的重要技能

儒枭:我看技术人的成长路径

阿里高级技术专家宋意:平凡人在阿里十年的成长之旅

阿里技术专家甘盘:浅谈双十一背后的支付宝LDC架构和其CAP分析

阿里技术专家光锥:亿级长连网关的云原生演进之路

阿里云原生张羽辰:服务发现技术选型那点事儿

蚂蚁研究员玉伯:做一个简单自由有爱的技术人

阿里高级技术专家至简: Service Mesh 在超大规模场景下的落地挑战

阿里巴巴山猎:手把手教你玩转全链路监控

阿里涉江:你真的会学习吗?从结构化思维说起

蚂蚁金服资深技术专家经国:云原生时代微服务的高可用架构设计

深入分布式缓存之EVCache探秘开局篇(文末赠书)

蚂蚁集团俞仁杰:金融级云原生之多活容器集群高可用建设实践

骆俊武:编程高手是如何练成的?

美团技术:复杂环境下落地 Service Mesh 的挑战与实践

Netstars CTO 陈斌:技术管理的两种思路

   END
#架构师必备#点分享点点赞点在看

惊了!同事竟然在代码里“下毒”相关推荐

  1. 同事又在代码里“下毒”,血压拉满...

    文章来源:[公众号:码农参上] 目录 前言 瞒天过海 舍近求远 颠倒黑白 化整为零 釜底抽薪 最后 前言 前几天,正巧赶上组里代码 review,一下午下来,感觉整个人都血压拉满了.五花八门的代码让我 ...

  2. 【Java】握草,你竟然在代码里下毒

    1.概述 转载:https://mp.weixin.qq.com/s?__biz=MzIxMDAwMDAxMw==&mid=2650728055&idx=1&sn=f220ff ...

  3. 握草,你竟然在代码里下毒!

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获!

  4. python安装库后无法调用摄像头_银川监控摄像头安装,有人在代码里下毒!慎用 pip install 命令...

    原标题:银川监控摄像头安装,有人在代码里下毒!慎用 pip install 命令 大约一年前,Python软件基金会(Python Software Foundation)发了一个需求咨询帖子(RFI ...

  5. 如何缺心眼的在代码里下毒

    偶然看到一篇脑洞大开的文章,转载过来乐呵一下,原文地址:https://www.jianshu.com/p/635fcf4fe594 下毒要点 独特的算法,个性的变量命名. 复杂的结构,畸形的文件路径 ...

  6. 有人在代码里下毒!慎用 pip install 命令

    大约一年前,Python软件基金会(Python Software Foundation)发了一个需求咨询帖子(RFI,https://discuss.python.org/t/what-method ...

  7. 如何编写无法维护的代码_如何写出让同事无法维护的代码?

    程序命名 容易输入的变量名.比如:Fred,asdf 单字母的变量名.比如:a,b,c,x,y,z(如果不够用,可以考虑 a1,a2,a3,a4,-.) 有创意地拼写错误.比如:SetPintleOp ...

  8. 【转】如何写出让同事无法维护的代码

    译者:陈皓 (@左耳朵耗子) 译文:http://coolshell.cn/articles/4758.html 对,你没看错,本文就是教你怎么写出让同事无法维护的代码. 一.程序命名 容易输入的变量 ...

  9. 我在暴躁同事小张的胁迫下学会了Go的交叉编译和条件编译

    今天继续关于Go开发经验的分享,这次的主题是关于Go的交叉编译和条件编译,伴随着我对自己打不过.惹不起的壕同事小张还有运维们的碎碎念. 交叉编译 交叉编译是用来在一个平台上生成另一个平台的可执行程序. ...

最新文章

  1. 世界人工智能大赛方案解析!
  2. 对我国6G早期研究布局的几点建议
  3. 如何打造真正可动态扩展的服务架构
  4. 2021夏季每日一题 【week2 未完结】
  5. 你要知道动机何在吗?
  6. I/O模型系列之四:两种高性能IO设计模式 Reactor 和 Proactor
  7. VMware 安装 CentOS 7
  8. 和我一起学Effective Java之创建和销毁对象
  9. 密码学朋克宣言(1993)
  10. 温度补偿计算公式_热补偿计算实例
  11. 2018总结----对共享单车的思考
  12. 两种降压型DC-DC变换器的工作原理
  13. 徐海学院军训计算机系,中gpi国矿业大学徐海学院09级军训队列会操评分表(.docx...
  14. WordPress 7B2主题插件分享 – 动漫视频在线播放插件Selection
  15. 计算机网络复习笔记——考试版
  16. ETC PSAM指令记录
  17. 基于规则的专家系统的图形检测
  18. 吃鸡游戏(18.11.24)
  19. 网站优化是如何进行的
  20. 巴黎时装周儿童单元深圳站代言人石雨薇,可爱演绎儿童时尚

热门文章

  1. 计算机组成原理艾列富,理论结合实验的计算机组成原理课程教学措施初探.pdf...
  2. androidstudio在mainactivity实现监听器借口无法抽象_趣操作,Tomcat如何实现一键式启停?
  3. TNN 量化_加量化港美股打新群!
  4. (计算机组成原理)第一章计算机系统概述-第二节:计算机硬件组成(存储器、运算器和控制器概述及计算机工作过程详解)
  5. Linux系统编程20:基础IO之从内核代码深刻理解Linux是如何管理文件及文件描述符的本质是什么
  6. 4-1:C/C++内存管理
  7. Qt之表单布局(QFormLayout)
  8. 27. 二叉树的镜像
  9. 从零开始学PowerShell(10)PowerShell中的子表达式
  10. C/C++日志写入系统log(/var/log/syslog)