当前位置: 首页 > 科技观察

惊讶!一个同事居然给代码“下毒”

时间:2023-03-15 17:21:00 科技观察

【.com原稿】写这篇文章是因为前段时间公司业务开发太忙太紧,所有开发都在加班赶项目,而且加入的新人少,造成了代码中一系列不可控的质量问题。图片来自Pexels文章,对这段时间代码中出现的各种问题进行了概述和梳理,主要集中在代码编码问题、抽象问题、调用相关的问题和写的问题接口。其实按理来说,这些都应该属于编程的基本功。看起来不值得写一篇文章,但是可以根据这些基本技巧来探讨一个代码编程系统的本质,值得展开。大概先打个基础,按照一个原则和建议,一个一个的讨论。编码问题避免过多的IF嵌套所谓“箭形”代码,基本上是大量IF嵌套造成的。一方面,它形成了一个深箭头形状,在阅读代码时造成夸张的缩进块。更要命的是嵌套层次越深,导致代码逻辑复杂度的加深。在读取第N层嵌套时,并不清楚可以进入什么逻辑,严重降低了代码的可读性和可维护性。其实对应的IF-ELSE太长的主要原因无非就是检查当前状态,决定是继续还是跳转。①使用GuardClauses提前返回,避免层层嵌套。首先分析一下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接口定义为:insertXXXupdateXXXlistXXX,无异于将DAO层直接移到RPC,直接暴露整个DAO,违反了微服务的接口调用原则。RPC接口只提供最原子的功能,限制消费者在生产者定义的业务中使用。严禁循环调用RPC接口。与项目中编程不同的是,每次调用RPC接口都会伴随网络开销。需要反复请求一个接口。这时可以要求RPC接口的提供者再提供一个可以批处理的接口,把单个重复的请求变成一个请求,减少网络开销。使用工具辅助清除恶意代码P3C插件在使用Eclipse或者idea编程时,推荐使用阿里的P3C插件进行辅助。代码规范检查插件P3C是由《阿里巴巴Java开发手册》改造而来的自动化插件。使用Skywalking查找恶意代码不同于P3C的直接辅助编码,Skywalking可以通过链接跟踪判断生产环境中某个微服务的接口性能或调动是否异常。这里就不介绍Skywalking的用处了。其实链路追踪不仅仅是运维或者架构师应该关注的点。普通开发者也可以使用链接追踪来追溯自己的代码,站在生产环境的高视角。检查要在链接中执行的代码。善于使用链接跟踪,往往可以发现在正常编码中被忽略的问题。例如,一个不经意的RPC循环调用很容易造成调用跨度很大,而开发者在编程时往往不能及时察觉。总结其实在分享的时候讲了抽象的原理和一些设计模式的使用,这里不再赘述。简单来说,要写出性能好、可读性高、逻辑清晰的代码,往往靠的不是一个个CURD,而是平时的总结和思考。作者:陈宇哲简介:十余年开发架构经验,国内较早的一批微服务开发实施者。曾在国内互联网公司网易、唯品会担任高级研发工程师,后在创业公司担任技术总监/架构师。编辑:陶佳龙征稿:如有意向投稿或寻求报道,请联系editor@51cto.com【原创稿件请注明原作者和出处为.com,合作网站转载】