后端开发怎么做得更优秀?记住这15个好习惯

news2024/11/15 17:19:07

目录

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

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

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

四. 封装方法形参

五. 封装通用模板

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

七. 保持优化性能的嗅觉

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

九. 会总结并使用工具类 

十. 控制方法函数复杂度

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

十二. 把日志打印好

十三. 考虑异常,处理好异常

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

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


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

  • 接口方法、类、复杂的业务逻辑,都应该添加有意义的注释
  1. 对于接口方法的注释,应该包含详细的入参和结果说明,有异常抛出的情况也要详细叙述;
  2. 类的注释应该包含类的功能说明、作者和修改者;
  3. 如果是业务逻辑很复杂的代码,真的非常有必要写清楚注释,越是复杂的代码逻辑,就越需要添加清楚详细的注释;
  •  清楚的注释,更有利于后面的维护

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

        目前大学生学做各种项目总会尝试去做各种各样的管理系统,基本上都是采用的 MVC 模式,也就是 controller、service、mapper、entity。如果我们所做系统未来有业务扩展,而我们没有提前拆分业务结构的话,很有可能就会发现,一个 service 包下,有上百个服务!!!

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

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

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


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

  • 远程操作或者数据库操作都是 比较耗网络、IO资源 的,所以尽量不在循环里远程调用、不在循环里操作数据库,能批量一次性去查回来尽量不要循环多次去查
  • 如果是操作数据库,也不要一次性差太多数据,可以分批500一次这样子;

正例:

remoteBatchQuery(param);

 反例:

for(int i=0;i<n;i++){
  remoteSingleQuery(param)
}

四. 封装方法形参

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

public void getUserInfo(String name,String age,String sex,String mobile,String idNo){
  // do something ...
}

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

public void getUserInfo(UserInfoParamDTO userInfoParamDTO){
  // do something ...
}

class UserInfoParamDTO{
  private String name;
  private String age; 
  private String sex;
  private String mobile;
  private String idNo;
}

五. 封装通用模板

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

我们来看一个业务需求:

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

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

  • 查询商户信息;

  • 对请求报文加签;

  • 发送 http 请求出去;

  • 对返回的报文验签。

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

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

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

abstract class AbstractMerchantService  { 

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

        然后所有商户接入,都做这个流程。如果这个通用模板是你抽取的,别的小伙伴接到开发任务,都是接入你的模板,你就是给别人提供方便的大佬了~~

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


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

        我们来看下这段代码: 

   public void test(UserStatus userStatus){
        if (userStatus != UserStatus.BANNED && userStatus != UserStatus.DELETED && userStatus != UserStatus.FROZEN) {
            //doSomeThing
            return
        }
    }

         这段代码有什么问题呢?

        是的,逻辑判断条件太复杂啦,我们可以封装一下它。如下:

public void test(UserStatus userStatus){
    if (isUserActive(userStatus)) {
        //doSomeThing
    }
}

private boolean isUserActive(UserStatus userStatus) {
        return userStatus != UserStatus.BANNED && userStatus != UserStatus.DELETED && userStatus != UserStatus.FROZEN;
}

七. 保持优化性能的嗅觉

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

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

        查用户信息200ms,查 banner 信息100ms、查弹窗信息50ms,那一共就耗时350ms了。如果还查其他信息,那耗时就更大了。

        如何优化它呢?

        可以并行发起,耗时可以降为200ms。如下:


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

        日常开发中,我们经常会遇到一些可变参数,比如 用户多少天没登录注销、运营活动或者不同节日红包皮肤切换、订单多久没付款就删除 等等。对于这些可变的参数,不应该直接写死代码。

        优秀的后端,要做配置化处理,可以把这些可变参数,放到数据库一个配置表里面,也可以放到项目的配置文件或者 Apollo 上。

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

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

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

        如果从一开始接口设计时,可以实现一张红包皮肤的配置表,将红包皮肤做成配置化呢?更换红包皮肤,只需修改一下表数据就好了。

        当然,还有一些场景适合一些配置化的参数:一个分页多少数量控制、某个抢红包多久时间过期,这些都可以搞到参数配置化表里面。这也是扩展性思想的一种体现。 

九. 会总结并使用工具类 

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

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

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

if (CollectionUtils.isEmpty(list)) {
   return null;
}

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


十. 控制方法函数复杂度

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

反例如下:

public class Test {
    private String name;
    private Vector<Order> orders = new Vector<Order>();

    public void printOwing() {
        //print banner
        System.out.println("****************");
        System.out.println("*****customer Owes *****");
        System.out.println("****************");

        //calculate totalAmount
        Enumeration env = orders.elements();
        double totalAmount = 0.0;
        while (env.hasMoreElements()) {
            Order order = (Order) env.nextElement();
            totalAmount += order.getAmout();
        }

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

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

public class Test {
    private String name;
    private Vector<Order> orders = new Vector<Order>();

    public void printOwing() {

        //print banner
        printBanner();
        //calculate totalAmount
        double totalAmount = getTotalAmount();
        //print details
        printDetail(totalAmount);
    }

    void printBanner(){
        System.out.println("****************");
        System.out.println("*****customer Owes *****");
        System.out.println("****************");
    }

    double getTotalAmount(){
        Enumeration env = orders.elements();
        double totalAmount = 0.0;
        while (env.hasMoreElements()) {
            Order order = (Order) env.nextElement();
            totalAmount += order.getAmout();
        }
        return totalAmount;
    }

    void printDetail(double totalAmount){
        System.out.println("name:" + name);
        System.out.println("amount:" + totalAmount);
    }   
}

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

        大家应该都有过这样的经历,windows系统桌面如果打开太多文件或者系统软件,就会觉得电脑很卡。

        当然,我们linux服务器也一样,平时操作文件或者数据库连接,IO资源流如果没关闭,那么这个IO资源就会被它占着,这样别人就没有办法用了,这就造成资源浪费。

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

FileInputStream fdIn = null;
try {
    fdIn = new FileInputStream(new File("/公众号_捡田螺的小男孩.txt"));
} catch (FileNotFoundException e) {
    log.error(e);
} catch (IOException e) {
    log.error(e);
}finally {
    try {
        if (fdIn != null) {
            fdIn.close();
        }
    } catch (IOException e) {
        log.error(e);
    }
}

十二. 把日志打印好

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

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

public void transfer(TransferDTO transferDTO){
    log.info("invoke tranfer begin");
    //打印入参
    log.info("invoke tranfer,paramters:{}",transferDTO);
    try {
      res=  transferService.transfer(transferDTO);
    }catch(Exception e){
     log.error("transfer fail,account:{}",
     transferDTO.getAccount())
     log.error("transfer fail,exception:{}",e);
    }
    log.info("invoke tranfer end");
    }

十三. 考虑异常,处理好异常

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

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

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

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

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

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

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

  • 注意异常对你的代码层次结构的浸染。

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

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

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


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

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

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

        因此,如果你的需求是在原来接口上修改,尤其这个接口是对外提供服务的话,一定要考虑接口兼容。

        举个例子吧,比如dubbo接口,原本是只接收A,B参数,现在加了一个参数C,就可以考虑这样处理:

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

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

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

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

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/56758.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

图的初识·遍历

文章目录深度优先搜索[DFS]实现代码广度优先搜索【BFS】思路图解代码实现广度优先遍历【BFS】图的结构深度优先搜索[DFS] 并不唯一&#xff0c;只是一种情况A−>IA->IA−>I 实现代码 使用邻接表表示图。遍历的时间复杂度O(VE)O(VE)O(VE)&#xff1b;邻接矩阵的时间…

使用 Python 和 OpenCV 制作反应游戏

在本文中&#xff0c;将向你展示如何使用 OpenCV 在 Python 中制作一个反应游戏&#xff0c;你可以动手来玩。你可能已经熟悉 OpenCV&#xff0c;OpenCV 基本上允许进行各种图像处理。你可以在下面的视频中看到最终结果&#xff0c;并且可以在此处获取文件&#xff1a;https://…

jsp4S店管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 4S店管理系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开 发&#xff0c;数据库为Mysql&#xff0c;使用ja…

海带软件分享——日常办公学习软件下载安装(百度网盘 | 收藏)

>>>深度学习Tricks&#xff0c;第一时间送达<<< &#x1f680; 写在前面 &#x1f431;‍&#x1f3cd; 本期开始&#xff0c;小海带会定期推荐一些日常办公学习软件及趣味网址&#xff08;内含安装教程&#xff09;&#xff0c;供大家学习交流参考 ~ 小伙…

三层交换技术

数据来源 一、三层交换技术出现背景&#xff1a; 解决之前给网段划分VLAN控制端口&#xff0c;然后使用路由器设置单臂路由的让不同VLAN可以通信的缺点&#xff0c; 单臂路由缺点&#xff1a; 1&#xff09;容易出现网络瓶颈 2&#xff09;容易发生单点物理故障 3&a…

[附源码]Python计算机毕业设计Django海南与东北的美食文化差异及做法的研究展示平台

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;我…

设计一个互联网交换设备的SNMP MIB库“X-MIB”

设计一个互联网交换设备的SNMP MIB库“X-MIB”&#xff1a; 问题描述 设备型号&#xff1a;字符串类型 设备生产厂商&#xff1a;字符串类型 设备名称&#xff1a;字符串类型 设备位置&#xff1a;字符串类型 设备运行时间&#xff1a;Timeticks 类型 设备队列数&#xff…

Find My资讯|苹果Find My帮助美警察逮捕连环盗车嫌犯

报道称&#xff0c;美国Shelby County Sheriff警方于11月13日搜查了Shady Ridge的5300街区的一处住宅&#xff0c;成功逮捕了现年20岁的嫌疑人Michael Walker。根据警方通告&#xff0c;该地区近期发生了多起盗车案件。而案件的突破口是发生在Pleasantwood 6800街区的一件盗车事…

【JUC】循环屏障 CyclicBarrier 详解

前言 jdk 中提供了许多的并发工具类&#xff0c;大家可能比较熟悉的有CountDownLatch&#xff0c;主要用来阻塞一个线程运行&#xff0c;直到其他线程运行完毕。而 jdk 还有一个功能类似并发工具类CyclicBarrier&#xff0c;你知道它的作用吗&#xff1f;和CountDownLatch有什…

MySQL学习记录(6)索引02

2.6、索引的使用 2.6.1、最左前缀法则 如果索引了多列&#xff08;联合索引&#xff09;&#xff0c;要遵循最左前缀法则。最左前缀法则指的是查询从索引的最左列开始&#xff0c;并且不跳过索引的列&#xff0c;如果跳跃某一列&#xff0c;索引将会部分失效&#xff08;后面…

大二Web课程设计:HTML+CSS学校静态网页设计——南京师范大学泰州学院(11页)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

[附源码]Python计算机毕业设计SSM开心鲜花系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Java外卖小程序管理系统源码带小程序前端+后端搭建教程

这套系统已经完成了线下配送的大部分功能 技术架构 技术框架&#xff1a;springboot ssm mysql redis 运行环境&#xff1a;IntelliJ IDEA 2022 jdk1.8 Mysql5.7.4 maven nginx 宝塔面板 后端搭建教程 1.下载源码后打开小皮面板&#xff0c;安装mysql5.7数据库&#x…

LIO-SAM源码解析(二):代码结构

1. 代码整体框架 首先看看工程目录结构&#xff0c;主要有五个文件&#xff0c;分别是utility.h&#xff0c;featureExtraction.cpp&#xff0c; imageProjection.cpp&#xff0c;imuPreintegration.cpp&#xff0c;mapOptmization.cpp LIO-SAM/config/params.yaml …

如何借助低代码开发平台 YonBuilder 填补应用开发 “产能缺口”?

低代码平台本身是各类模型、引擎的重新组合&#xff0c;目的是为了填补应用开发需求远超开发者产能这一缺口&#xff0c;那如何理解填补应用开发需求和开发者产能的缺口&#xff1f; 完成一个企业级的应用复杂度随着技术的进步、需求的细化、业务要求的变化并不是逐渐降低而是…

原创|对接三方服务商回调鉴权的程序代码设计

文章目录一、背景二、详细设计1、UML设计2、程序设计2.1、AuthenticateActionEnum2.2、AuthenticateDispatcher2.3、BaseAuthenticateContext<Request>2.3.1、ActivityStatusChangeAuthenticateContext2.3.2、VodEventNotifyAuthenticateContext2.4、AbstractAuthenticat…

【Python+Appium】自动化测试(十一)location与size获取元素坐标

目录 前言 一&#xff0c;获取元素坐标的方法 1&#xff0c;size获取元素的宽、高 2&#xff0c;location获取元素左上角坐标 3&#xff0c;由此可以计算出元素其他的坐标 二&#xff0c;使用场景 结语 前言 appium做app自动化测试过程中&#xff0c;有时需要获取控件元…

git clean 命令详解

1. git clean 介绍 2. git clean 使用 3. clean 和 reset 命令 1. git clean 介绍 git clean 命令用于删除工作目录中没有被 tracked 的文件 这个命令很多人都不知道&#xff0c;也不去用它&#xff0c;而是通过手动去删除这些文件 这个命令一定要慎用&#xff0c;当你对这…

智源社区AI周刊No.108:Meta发布玩外交游戏的Cicero,登Science;Neuralink实现猴子意念打字...

汇聚每周AI热点&#xff0c;不错过重要资讯&#xff01;欢迎扫码&#xff0c;关注并订阅智源社区AI周刊。Meta发布外交谈判策略系统Cicero&#xff1a;模拟人类参与战略决策&#xff0c;已登《Science》期刊近日&#xff0c;Meta发布人工智能系统Cicero&#xff0c;该系统结合神…

【云原生】k8s 管理平台 rancher

文章目录一、概述二、Rancher 架构三、安装 Rancher1&#xff09;安装Helm2&#xff09;安装ingress-controller3&#xff09;为 Rancher 创建命名空间4&#xff09;选择 SSL 配置5&#xff09;安装 cert-manager6&#xff09;通过 Helm 安装 Rancher2&#xff09;添加 Helm Ch…