0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

15条重写烂代码的经验分享

jf_ro2CN3Fa 来源:捡田螺的小男孩 作者:捡田螺的小男孩 2022-11-11 11:10 次阅读

写这篇文章,来记录一下一个优秀的后端开发程序员,应该有哪些好的开发习惯。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

项目地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro

视频教程:https://doc.iocoder.cn/video/

1.注释尽可能全面,写有意义的注释

接口方法、类、复杂的业务逻辑,都应该添加有意义的注释

对于接口方法的注释,应该包含详细的入参和结果说明,有异常抛出的情况也要详细叙述

类的注释应该包含类的功能说明、作者和修改者。

如果是业务逻辑很复杂的代码,真的非常有必要写清楚注释。

清楚的注释,更有利于后面的维护。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

项目地址:https://gitee.com/zhijiantianya/yudao-cloud

视频教程:https://doc.iocoder.cn/video/

2.项目拆分合理的目录结构

记得读大学那会,刚学做各种各样的管理系统,都是用MVC模式,也就是controller、service、mapper、entity。如果未来业务扩展,你没有拆分业务结构的话,很可能就会发现,一个service包下,有上百个服务。。。

正确的做法,如果服务过多,应该根据不同的业务进行划分,比如订单、登陆、积分等等

487a6840-6167-11ed-8abf-dac502259ad0.png

当然,你也可以根据不同的业务划分模块,比如建一个moudles包,然后按订单、登陆等业务划分,每个业务都有自己的controller、service、mapper、entity。

我们拆分的目的,就是让项目结构更清晰,可读性更强,更容易维护 而已。

3. 不在循环里远程调用、或者数据库操作,优先考虑批量进行。

远程操作或者数据库操作都是比较耗网络、IO资源 的,所以尽量不在循环里远程调用、不在循环里操作数据库,能批量一次性查回来尽量不要循环多次去查 。(但是呢,如果是操作数据库,也不要一次性查太多数据哈,可以分批500一次酱紫)。

正例:

remoteBatchQuery(param);

反例:

for(inti=0;i

4. 封装方法形参

如果你的方法参数过多,要封装一个对象出来。反例如下:

publicvoidgetUserInfo(Stringname,Stringage,Stringsex,Stringmobile,StringidNo){
//dosomething...
}

如果参数很多,做新老接口兼容处理也比较麻烦。建议写个对象出来,如下:

publicvoidgetUserInfo(UserInfoParamDTOuserInfoParamDTO){
//dosomething...
}

classUserInfoParamDTO{
privateStringname;
privateStringage;
privateStringsex;
privateStringmobile;
privateStringidNo;
}

5. 封装通用模板

一个优秀的后端开发,应该具备封装通用模板 的编码能力。

我们来看一个业务需求:假设我们有这么一个业务场景:内部系统不同商户,调用我们系统接口,去跟外部第三方系统交互(http方式)。走类似这么一个流程,如下:

4889a2e2-6167-11ed-8abf-dac502259ad0.png

一个请求都会经历这几个流程:

查询商户信息

对请求报文加签

发送http请求出去

对返回的报文验签

通过HTTP发请求出去时,有的商户可能是走代理 的,有的是走直连。假设当前有A,B商户接入,不少伙伴可能这么实现,伪代码如下:

//商户A处理句柄
CompanyAHandlerimplementsRequestHandler{
Resphander(req){
//查询商户信息
queryMerchantInfo();
//加签
signature();
//http请求(A商户假设走的是代理)
httpRequestbyProxy()
//验签
verify();
}
}
//商户B处理句柄
CompanyBHandlerimplementsRequestHandler{
Resphander(Rreq){
//查询商户信息
queryMerchantInfo();
//加签
signature();
//http请求(B商户不走代理,直连)
httpRequestbyDirect();
//验签
verify();
}
}

假设新加一个C商户接入,你需要再实现一套这样的代码。显然,这样代码就重复了。这时候我们可以封装一个通用模板 !我们就可以定义一个抽象类,包含请求流程的几个方法,伪代码如下:

abstractclassAbstractMerchantService{

//模板方法流程
ResphandlerTempPlate(req){
//查询商户信息
queryMerchantInfo();
//加签
signature();
//http请求
httpRequest();
//验签
verifySinature();
}
//Http是否走代理(提供给子类实现)
abstractbooleanisRequestByProxy();
}

然后所有商户接入,都做这个流程。如果这个通用模板是你抽取的,别的小伙伴接到开发任务,都是接入你的模板,是不是会有点自豪呀,哈哈~

封装通用模板 ,就是抽个模板模式嘛?其实不仅仅是,而是自己对需求、代码的思考与总结 ,一种编程思想的升华

6. 封装复杂的逻辑判断条件

我们来看下这段代码:

publicvoidtest(UserStatususerStatus){
if(userStatus!=UserStatus.BANNED&&userStatus!=UserStatus.DELETED&&userStatus!=UserStatus.FROZEN){
//doSomeThing
return
}
}

这段代码有什么问题呢?是的,逻辑判断条件太复杂啦,我们可以封装一下它 。如下:

publicvoidtest(UserStatususerStatus){
if(isUserActive(userStatus)){
//doSomeThing
}
}

privatebooleanisUserActive(UserStatususerStatus){
returnuserStatus!=UserStatus.BANNED&&userStatus!=UserStatus.DELETED&&userStatus!=UserStatus.FROZEN;
}

7. 保持优化性能的嗅觉

优秀的后端开发,应该保持优化性能的嗅觉。比如避免创建比必要的对象、异步处理、使用缓冲流,减少IO操作等等。

比如,我们设计一个APP首页的接口,它需要查用户信息、需要查banner信息、需要查弹窗信息等等。假设耗时如下:

48af2dbe-6167-11ed-8abf-dac502259ad0.png

查用户信息200ms,查banner信息100ms、查弹窗信息50ms,那一共就耗时350ms了。如果还查其他信息,那耗时就更大了。如何优化它呢?可以并行发起,耗时可以降为200ms。如下:

48c6d89c-6167-11ed-8abf-dac502259ad0.png

8. 可变参数的配置化处理

日常开发中,我们经常会遇到一些可变参数,比如用户多少天没登录注销、运营活动,不同节日红包皮肤切换、订单多久没付款就删除等等。对于这些可变的参数,不用该直接写死在代码。优秀的后端,要做配置化处理,你可以把这些可变参数,放到数据库一个配置表里面,也可以放到项目的配置文件或者apollo上。

比如产品经理提了个红包需求,圣诞节的时候,红包皮肤为圣诞节相关的,春节的时候,为春节红包皮肤等。如果在代码写死控制,可有类似以下代码:

if(duringChristmas){
img=redPacketChristmasSkin;
}elseif(duringSpringFestival){
img=redSpringFestivalSkin;
}

如果到了元宵节的时候,运营小姐姐突然又有想法,红包皮肤换成灯笼相关的,这时候,是不是要去修改代码了,重新发布了?

从一开始接口设计时,可以实现一张红包皮肤的配置表 ,将红包皮肤做成配置化呢?更换红包皮肤,只需修改一下表数据就好了。当然,还有一些场景适合一些配置化的参数:一个分页多少数量控制、某个抢红包多久时间过期这些,都可以搞到参数配置化表里面。这也是扩展性思想的一种体现。

9. 会总结并使用工具类。

很多小伙伴,判断一个list是否为空,会这么写:

if(list==null||list.size()==0){
returnnull;
}

这样写呢,逻辑是没什么问题的。但是更建议用工具类,比如:

if(CollectionUtils.isEmpty(list)){
returnnull;
}

日常开发中,我们既要会用工具类,更要学会自己去总结工具类。比如去文件处理工具类、日期处理工具类等等。这些都是优秀后端开发的一些好习惯。

10. 控制方法函数复杂度

你的方法不要写得太复杂,逻辑不要混乱,也不要太长 。一个函数不能超过80行。写代码不仅仅是能跑就行,而是为了以后更好的维护。

反例如下:

publicclassTest{
privateStringname;
privateVectororders=newVector();

publicvoidprintOwing(){
//printbanner
System.out.println("****************");
System.out.println("*****customerOwes*****");
System.out.println("****************");

//calculatetotalAmount
Enumerationenv=orders.elements();
doubletotalAmount=0.0;
while(env.hasMoreElements()){
Orderorder=(Order)env.nextElement();
totalAmount+=order.getAmout();
}

//printdetails
System.out.println("name:"+name);
System.out.println("amount:"+totalAmount);
......
}
}

其实可以使用Extract Method,抽取功能单一的代码段,组成命名清晰的小函数,去解决长函数问题,正例如下:

publicclassTest{
privateStringname;
privateVectororders=newVector();

publicvoidprintOwing(){

//printbanner
printBanner();
//calculatetotalAmount
doubletotalAmount=getTotalAmount();
//printdetails
printDetail(totalAmount);
}

voidprintBanner(){
System.out.println("****************");
System.out.println("*****customerOwes*****");
System.out.println("****************");
}

doublegetTotalAmount(){
Enumerationenv=orders.elements();
doubletotalAmount=0.0;
while(env.hasMoreElements()){
Orderorder=(Order)env.nextElement();
totalAmount+=order.getAmout();
}
returntotalAmount;
}

voidprintDetail(doubletotalAmount){
System.out.println("name:"+name);
System.out.println("amount:"+totalAmount);
}
}

11. 在finally块中对资源进行释放

应该大家都有过这样的经历,windows系统桌面如果打开太多文件或者系统软件,就会觉得电脑很卡。当然,我们linux服务器也一样,平时操作文件,或者数据库连接,IO资源流如果没关闭,那么这个IO资源就会被它占着,这样别人就没有办法用了,这就造成资源浪费。

我们操作完文件资源,需要在在finally块中对资源进行释放。

FileInputStreamfdIn=null;
try{
fdIn=newFileInputStream(newFile("/捡田螺的小男孩.txt"));
}catch(FileNotFoundExceptione){
log.error(e);
}catch(IOExceptione){
log.error(e);
}finally{
try{
if(fdIn!=null){
fdIn.close();
}
}catch(IOExceptione){
log.error(e);
}
}

12.把日志打印好

日常开发中,一定需要把日志打印好。比如:你实现转账业务,转个几百万,然后转失败了,接着客户投诉,然后你还没有打印到日志,想想那种水深火热的困境下,你却毫无办法。。。

一般情况,方法入参、出参需要打印日志,异常的时候,也要打印日志等等,如下:

publicvoidtransfer(TransferDTOtransferDTO){
log.info("invoketranferbegin");
//打印入参
log.info("invoketranfer,paramters:{}",transferDTO);
try{
res=transferService.transfer(transferDTO);
}catch(Exceptione){
log.error("transferfail,account:{}",
transferDTO.getAccount())
log.error("transferfail,exception:{}",e);
}
log.info("invoketranferend");
}

13. 考虑异常,处理好异常

优秀的后端开发,应当考虑到异常,并做好异常处理。田螺哥给大家提了10个异常处理的建议:

尽量不要使用e.printStackTrace(),而是使用log打印。因为e.printStackTrace()语句可能会导致内存占满。

catch住异常时,建议打印出具体的exception,利于更好定位问题

不要用一个Exception捕捉所有可能的异常

记得使用finally关闭流资源或者直接使用try-with-resource。

捕获异常与抛出异常必须是完全匹配,或者捕获异常是抛异常的父类

捕获到的异常,不能忽略它,至少打点日志吧

注意异常对你的代码层次结构的侵染

自定义封装异常,不要丢弃原始异常的信息Throwable cause

运行时异常RuntimeException ,不应该通过catch的方式来处理,而是先预检查,比如:NullPointerException处理

注意异常匹配的顺序,优先捕获具体的异常

14. 考虑系统、接口的兼容性

优秀的后端开发,会考虑系统、接口的兼容性。

如果修改了对外旧接口,但是却不做兼容。这个问题可能比较严重,甚至会直接导致系统发版失败的。新手程序员很容易犯这个错误哦~

因此,如果你的需求是在原来接口上修改,尤其这个接口是对外提供服务的话,一定要考虑接口兼容。举个例子吧,比如dubbo接口,原本是只接收A,B参数,现在你加了一个参数C,就可以考虑这样处理:

//老接口
voidoldService(A,B){
//兼容新接口,传个null代替C
newService(A,B,null);
}

//新接口,暂时不能删掉老接口,需要做兼容。
voidnewService(A,B,C){
...
}

15. 采取措施避免运行时错误

优秀的后端开发,应该在编写代码阶段,就采取措施,避免运行时错误 ,如数组边界溢出,被零整除,空指针等运行时错误。类似代码比较常见:

Stringname=list.get(1).getName();//list可能越界,因为不一定有2个元素哈

所以,应该采取措施,预防一下数组边界溢出,正例如下:

if(CollectionsUtil.isNotEmpty(list)&&list.size()>1){
Stringname=list.get(1).getName();
}





审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux系统
    +关注

    关注

    4

    文章

    590

    浏览量

    27330
  • MVC
    MVC
    +关注

    关注

    0

    文章

    73

    浏览量

    13838
  • HTTP协议
    +关注

    关注

    0

    文章

    61

    浏览量

    9697

原文标题:用1个月重构了同事写的烂代码,我总结出了15条重写烂代码的经验!

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    100FPGA经验

    100FPGA经验 非常值得收藏 100FPGA经验 非常值得收藏
    发表于 11-11 17:01 23次下载

    总结模拟电路设计经验12

    模拟电子的相关知识学习教材资料——总结模拟电路设计经验12
    发表于 09-20 16:10 6次下载

    模拟电路设计经验12

    模拟电子的相关知识学习教材资料——模拟电路设计经验12
    发表于 09-27 15:19 0次下载

    代码你能忍吗?优秀的代码VS糟糕的代码

    糟糕的代码原来那么不堪一击。
    的头像 发表于 03-30 10:09 4366次阅读
    <b class='flag-5'>烂</b><b class='flag-5'>代码</b>你能忍吗?优秀的<b class='flag-5'>代码</b>VS糟糕的<b class='flag-5'>代码</b>

    垃圾代码应该怎么写

    在 GitHub 上有一个新项目,它描述了「最佳垃圾代码」的十九关键准则。从变量命名到注释编写。这些准则将指导你写出最亮眼的代码。 为了保持与原 GitHub 项目一致的风格,下文
    的头像 发表于 01-18 11:08 1967次阅读

    高速PCB布线经验分享

    让你布线少走弯道的15高速PCB布线经验分享
    的头像 发表于 02-12 10:44 5822次阅读

    15高速PCB布线经验分享

    让你布线少走弯道的15高速PCB布线经验分享
    发表于 03-04 06:24 31次下载
    <b class='flag-5'>15</b><b class='flag-5'>条</b>高速PCB布线<b class='flag-5'>经验</b>分享

    STM32 40知识,开发经验必备资料下载

    电子发烧友网为你提供STM32 40知识,开发经验必备资料下载的电子资料下载,更有其他相关的电路图、源代码、课件教程、中文资料、英文资料、参考设计、用户指南、解决方案等资料,希望可以帮助到广大的电子工程师们。
    发表于 04-04 08:48 24次下载
    STM32 40<b class='flag-5'>条</b>知识,开发<b class='flag-5'>经验</b>必备资料下载

    PCB电源设计13经验资料下载

    电子发烧友网为你提供PCB电源设计13经验资料下载的电子资料下载,更有其他相关的电路图、源代码、课件教程、中文资料、英文资料、参考设计、用户指南、解决方案等资料,希望可以帮助到广大的电子工程师们。
    发表于 04-21 08:54 16次下载
    PCB电源设计13<b class='flag-5'>条</b><b class='flag-5'>经验</b>资料下载

    PID算法原理_调试经验以及代码总结

    PID算法原理_调试经验以及代码总结分享。
    发表于 05-25 15:59 16次下载

    PID算法原理、调试经验以及代码资料

    PID算法原理、调试经验以及代码资料
    发表于 11-21 10:25 15次下载

    Rust重写的LSP:KCL IDE 插件的功能介绍与设计解析

    在这次更新中,我们发布了全新的 KCL VS Code 插件,并且用 Rust 重写了 LSP 的 Server 端。我们提供了 IDE 中常用的代码辅助功能,如高亮、跳转、补全、Outline、悬停、错误提示等。
    的头像 发表于 05-11 09:39 932次阅读
    Rust<b class='flag-5'>重写</b>的LSP:KCL IDE 插件的功能介绍与设计解析

    Windows 11初尝Rust,36000行内核代码重写

    更早些时候,微软用 Rust 重写了 DirectWrite Core 库的概念验证,它是 Windows 的 DWrite 引擎的 Windows App SDK 实现,用于文本分析、布局和渲染
    的头像 发表于 05-19 16:39 987次阅读
    Windows 11初尝Rust,36000行内核<b class='flag-5'>代码</b>已<b class='flag-5'>重写</b>!

    大彩串口屏控件教程15 - 圆形进度控件应用

    大彩串口屏控件教程15-圆形进度控件应用
    发表于 04-29 12:57 3次下载

    【AWTK使用经验】如何设计立体电池进度

    AWTK是基于C语言开发的跨平台GUI框架。《AWTK使用经验》系列文章将介绍开发AWTK过程中一些常见问题与解决方案,例如:如何加载外部资源?如何设计自定义进度?这些都会在系列文章进行解答
    的头像 发表于 04-18 08:25 409次阅读
    【AWTK使用<b class='flag-5'>经验</b>】如何设计立体电池进度<b class='flag-5'>条</b>?