企业级开发项目和自学项目到底有什么区别

news2024/11/15 13:40:30

前言

好久不见了各位!最近几个月都未更新,是因为从春招开始就在投简历面试实习岗位,然后入职,最后成功成为了一个半成品后端练习生,想说的话有太多太多
下面就站在一个在校实习生的身份,结合自己最近几个月来在企业开发环境中写代码的经历,与即将上岗,或者还在期待上岗的小伙伴们来分享分享——企业级项目与网络自学项目究竟有什么区别
在这里插入图片描述
公司主流技术栈:
SpringBoot+Dubbo+Flink+Kalfk+MyBatisPlus+Mysql+Redis+Seata+MongoDB+ES+React+区块链+人工智能

1.复杂的业务逻辑

在生产与开发环境之间往往还有一个qa环境(质量保障),当进入公司的第一天,连上了qa环境的数据库,给我的第一感受就是:“卧槽,这表里的字段也太多了吧“
繁多的字段对应的则是复杂的业务逻辑,实体类也不再具有单一属性,更多的则是不同的实体相互组合,返回多方作用的结果:
对于某些业务场景MybatisPlus的Wrapper存在一定的局限性,需要我们自己手动封装查表的方法。比如查询要使用稀奇古怪的函数、多表查询,连接查询,子查询等等…
现在我就来举两个我在开发过程中写出的两个Mapper来给大家看看,开开胃:
1.各种函数的灵活使用:
在这里插入图片描述
2.复杂的条件判断:
在这里插入图片描述

2.严格的参数校验

对于特殊数据的格式企业都会有统一的要求,因此在前端控制器(Controller)里要对接收到的参数做相应的参数校验
1.时间类型参数的校验
在这里插入图片描述
2.手机号类型参数的校验
有人会问手机号这种数据前端不是会帮我们校验吗?为什么后端还要重复工作,其实这是因为我们要保证测试用例的成功执行,避免在使用PostMan等接口测试工具的情况下拿到token之后从而绕开了前端,将不正确的数据请求到后端服务器
在这里插入图片描述
3.分页参数的校验
分页的大小也要控制在合理的范围,分页数过大,或者不合常理都会导致出人意外的错误。比如传递一个超大的pageSize参数,并且库中的数据量很大的情况下,会导致一次请求查出海量数据。往复几次会占用大量的服务器带宽,最终让服务器挂掉~
在这里插入图片描述

3.生产环境对接口性能要求

1.循环查库使不得
举一个大家耳熟能详的例子——瑞吉外卖,这应该能算得上是自学项目的代表吧,我们来看一下其中一个接口的写法:

    @GetMapping("/page")
    public R<Page> page(int page, int pageSize, String name) {
        log.info("查询的名称是:{}", name);

        //分页构造器
        Page<Dish> dishPage = new Page<>(page, pageSize);

        Page<DishDto> dishDtoPage = new Page<>();
        //条件构造器
        LambdaQueryWrapper<Dish> dishLqw = new LambdaQueryWrapper<>();
        //模糊查询,将前端输入的名字和Dish表中的name进行模糊查询并添加name不为空的条件
        dishLqw.like(name != null, Dish::getName, name);
        dishLqw.orderByDesc(Dish::getUpdateTime);

        //调用Service查询
        dishService.page(dishPage, dishLqw);

        BeanUtils.copyProperties(dishPage, dishDtoPage, "records");
        List<Dish> records = dishPage.getRecords();

        List<DishDto> dtoList = records.stream().map((temp) ->{
            DishDto dishDto = new DishDto();

            //再次拷贝,将普通属性拷贝给dishDto
            BeanUtils.copyProperties(temp, dishDto);

            Long categoryId = temp.getCategoryId();  //拿到分类id
            // 拿到分类对象,根据id查到对象
            Category category = categoryService.getById(categoryId);

            if(category!=null) { //避免出现空指针
                //通过分类对象拿到name
                String categoryName = category.getName();
                //把name设置给dishDto
                dishDto.setCategoryName(categoryName);//设置dishDto中CategoryName的值
            }
            return dishDto;
        }).collect(Collectors.toList());

        dishDtoPage.setRecords(dtoList);

        return R.success(dishDtoPage);
    }

细心的同学肯定会发现,在我们的流里针对每一次流中的元素都进行了一次查库的操作(i/o)。
Category category = categoryService.getById(categoryId);
在数据量非常小的情况下可能接口性能不会有什么影响,但流中数据如果很大,那这样的接口耗时肯定是非常长的,绝对是不会上生产环境的。这就是企业与自学最大的区别体现之一。
所以,为了避免这样的情况发生,我们会尽量减少查库的频率,就类似下面这样的写法(非常相似的场景,也是我入职的第一个接口):
在这里插入图片描述
总而言之就一句话:能一次全部查出来尽量不要一次一次查
以后大家要是看到接口里存在循环交互数据库的地方,那这接口八成是个毒瘤
2.多线程异步处理
其实有一些场景对同步性的要求并非很强,同步处理反而会增加很多无效的等待时间。对于一些"不需要等待结果返回的地方"大胆可以进行异步处理。
举一个例子,现在的任务是根据一批未缴费的账单向业主一键推送消息,每当推送完成要更新提醒状态。而更新和推送都有很多前置数据要去准备,并且推送业务的调用链很长。如果是单线程的情况方法从上到下执行接口,耗时大概是5-6s,没有异步调用的机制在推送的时间内方法的调用者什么都做不了, 其他代码都按下了暂停键。但是当我使用线程池异步去一键推送消息,并更新状态就会减少大量的等待时间,接口的耗时也降低到了一秒以内!
在这里插入图片描述
由此可见,对于一些特殊的场景,采用异步还是同步的方式真的非常重要。而多线程只是人让我们实现异步的一种手段之一

4.编码规范

4.1流式编程

以单个微服务为单位分库的特性导致了很多强相关联的数据不在一个数据库里,这样我们就失去了很多的多表联查机会。
相反,更多则是处理、、组装多个来自不同微服务接口的数据,而这个过程中就会不禁用到流式编程。
学会并且习惯使用流式编程真的可以让自己开发的如鱼得水,流式写法+Lambda表达式我愿称之为绝杀,不仅可以简化业务代码更让让开发者的思路更加清晰
下面我就举几个我入职后用到流式开发、lambda表达式的几个场景:
1.过滤去重List

List<Long> communityIds = transactionList.stream()
   .map(TransactionCommunityVO::getPayeeCommunityId)
   .filter(ObjectUtils::isNotEmpty).distinct()
   .collect(Collectors.toList());

2.MyBatisPlus使用lambda表达式来进行“选择模糊组合查询”

LambdaQueryChainWrapper<Device> deviceLambdaQueryChainWrapper = iDeviceService.lambdaQuery();
    if (ObjectUtils.isNotEmpty(deviceQuery2.getFuzz())) {
     deviceLambdaQueryChainWrapper.and(x -> {
       x.like(Device::getName, deviceQuery2.getFuzz()).or().like(Device::getCode1, deviceQuery2.getFuzz());
      });
}

3.通过分组的方法对List中“某字段的值和该字段出现的次数”以 < k-v > map的方式进行收集

  Map<Long, Long> idCountMap = deviceVo2s.stream()
  .filter(index -> ObjectUtils.isNotEmpty(index.getOwnerId()))
  .collect(Collectors.groupingBy(DeviceVo2::getOwnerId, Collectors.counting()));

4.流里处理字符串

        list= list.stream().map(x -> {
            String[] strings = new String[x.length];
            for (int i = 0; i < x.length; i++) {
                if (null == x[i]) {
                    x[i] = "";
                }
                strings[i] = x[i].trim();
            }
            return strings;
        }).collect(Collectors.toList());

5.流里收集指定条件的数据

Map<String, String> map2 = list4.stream().filter(temp -> "男".equals(temp.split("-")[1]))
                .collect(Collectors.toMap(
                        temp -> temp.split("-")[0]
                        ,
                        temp -> temp.split("-")[2]));

4.2复用性要求

一个好的接口不仅仅局限于现有的功能,更多的是要做到向上兼容。复用性的高低很大程度上决定了业务代码的篇幅。在业务开发中经常会看到底层的一个接口左上角显示“xxx次用法”而这样的接口就拥有会很高的复用性。
对于如何个提升代码复用性,这里,举两个我感触很深的地方。
1.减少不必要的参数传递:
有大量参数需要传递的方法,通常很难阅读。我们可以将所有参数封装到一个对象中来完成对象的传递,要用到时直接get就好,这也有利于错误跟踪。许多开发因为太多层的对象包装对系统效率有影响。但是,和它带来的好处相比,我们宁愿做包装。毕竟,"封装"也是OOP的基本特性之一,而且,“每个对象完成尽量少(而且简单)的功能”,也是OOP的一个基本原则。
2.尽可能地去解耦:
"将可变的部分和不可变的部分分离"是面向对象设计的第三个原则。如果使用继承的复用技术,我们可以在抽象基类中定义好不可变的部分,而由其子类去具体实现可变的部分,不可变的部分不需要重复定义,而且便于维护。如果使用对象组合的复用技术,我们可以定义好不可变的部分,而可变的部分可以由不同的组件实现,根据需要,在运行时动态配置。这样,我们就有更多的时间关注可变的部分。对于对象组合技术而言,每个组件只完成相对较小的功能,相互之间耦合比较松散,复用率较高,通过组合,就能获得新的功能。
3.底层方法少写固定条件:
特别是在Mapper或者Wapper方法里,尽可能地去少写不必要的定值查询条件,比如限定状态,类型等方面,留出一个可更改的传递参数去向上兼容。

4.3枚举类的使用

一年前刚学习到枚举时,别人问我,你怎么看待枚举这个类。我说他就是一个定义规范的类
在这里插入图片描述
现在到了企业开发中,这一点表现得更加明显。很多时候,数据库里的一些字段往往都会有很多值。比如,表里的status这一字段,它的值可能是0,1,2或者3…不同的值代表这条数据不同的状态。
在这里插入图片描述
在数据处理的过程中,遇到对状态的限定就不要再去用xxx=0,xxx=2…取而代之的是xxx=StatusEnum.ONLINE.getValue()这样做不仅更加直观让别的开发一看就明白,而且还做到了某种意义上的解耦!
把公用的放在一个地方,这种场景是不是很像配置文件。想当初IOC就是靠的一手DOM4J和XML

4.4查询类的使用

企业级的项目往往需求十分复杂,就比如一个查询的接口可能要接收很多的参数,这些字段参数从Controller层下来,逐层向下传递可能就会导致一个不好的现象——实现方法接收的形参太多!
就像这样:

PageInfo<Device> getDeviceList(Integer current, Integer size, List<Long> ownerIds, List<DeviceTypeEnum> typeList, 
String code1, String code2, List<Integer> providerList, List<IsDeletedType> isDeletedList, List<Long> communityIdList,
 String nameLike, List<Long> physicalCommunityIdList, List<Integer> statusList, List<Long> parentIdList, List<Long>
  idList, List<String> areaPathList, List<String> sortList, Sort.Direction direction, List<String> communityPathList, 
  List<DeviceSubTypeEnum> deviceSubTypeEnumList, String code1Like, String code2Like);

这形参是不是老长了~这样的写法看起来“中规中矩”,但是调用起来特别费眼睛,一个参数传错位置就得调试半天!
在这里插入图片描述
所以,为了解决这样的问题,我们使用查询类来进行查询(查询类就是把属性字段放到实体类里封装好,并通过set、get方法来操作属性),会使接口方法看起来简洁很多,并且大大降低了出错的概率,就像这样:
在这里插入图片描述

4.5RestFul风格

以前在自学SpringMVC时就讲到RestFul风格的应用,当时没接触到企业级项目觉得这种风格的东西无关紧要,但其实不然,接口遵循这样的风格规范十分重要!
客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。在企业级应用中往往一个模块有好几十个Controller,当你去其中浏览,你会发现在仅看了请求方式(post/get/delete/update)和请求路径之后就会了解到这个Controller方法的大致功能。RestFul风格无疑是一种优良的开发规范!

4.6注释

Last but not least!
切记写代码要记得写注释,所谓注释绝对不是写什么注释什么,而对关键地方进行的一种声明。
比如:
1.接口的具体功能的说明
2.形参传递的说明
3.返回值的类型以及内容的说明
4.实体类中属性字段的说明
5.共性字段特殊使用的说明

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

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

相关文章

科普 | 眼图

本文简要说明眼图相关的知识&#xff0c;参考是德科技的文章 1。 科普 | 眼图 基本知识串扰眼图眼图的产生原理及作用创建眼图 - 眼图波形的采样过程眼图的产生原理及作用眼图可以看出哪些性能指标&#xff1f;如何评判眼图质量&#xff1f;眼图测试模板眼图与存储深度实时的眼…

短视频seo源码开发部署技术解析

短视频seo开发需要哪些技术 应用程序优化技术&#xff1a;包括应用程序的各种元素&#xff08;如标题、描述、关键字、图标等&#xff09;的优化和设置&#xff0c;以及应用程序内部链接和导航的合理布局和设置。 视频内容优化技术&#xff1a;包括视频标题、描述、标签、封面…

人工智能数据集处理——数据获取

目录 1、从csv和txt文件中读取数据 pandas中可使用read_csv读取csv或txt文件中的数据 使用read_csv()函数读取phones.csv文件中的数据,并指定编码格式为gbk 使用head()方法指定获取phones.csv文件中前3行的数据 使用read_csv() 函数读取 itheima_books.txt文件中的数据,并指…

【Redis】2、Redis 的 Java 客户端(Jedis 和 SpringDataRedis)

目录 零、Redis 的 Java 客户端有哪些&#xff1f;二、Jedis 客户端(1) 引依赖(2) 连接 Redis 服务并测试(3) Redis 连接池 三、SpringDataRedis 介绍四、SpringBoot 中集成 SpringDataRedis(1) 引入依赖(2) 配置文件中书写相关配置(3) RedisTemplate 的默认序列化方式(4) 自定…

高校学生考勤系统

摘 要 在Internet高速发展的今天&#xff0c;我们生活的各个领域都涉及到计算机的应用&#xff0c;其中包括高校学生考勤系统的网络应用&#xff0c;在外国高校学生考勤系统已经是很普遍的方式&#xff0c;不过国内的高校学生考勤可能还处于起步阶段。高校学生考勤系统具有管理…

Linux网络-数据链路层,MAC帧解析,ARP协议

目录 数据链路层VS网络层 以太网概念 以太网的帧格式&#xff08;报文格式&#xff09;&#xff08;也可以称之为MAC帧&#xff09; MAC地址的概念 MAC帧格式 局域网通信原理 MTU MTU说明 MTU对IP协议的影响 MTU对UDP协议的影响 MTU对TCP协议的影响 ARP协议 ARP协…

【算法题解】41. 二叉树的中序遍历

这是一道 简单 题 https://leetcode.cn/problems/binary-tree-inorder-traversal/ 题目 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,3,2] 示例 2&#xff1a; 输入&#…

[易语言][部署]使用易语言部署paddleocr的onnx模型api接口推理直接调用

易语言如何部署paddleocr模型&#xff0c;如今paddleocr模型广泛被使用各种编程&#xff0c;为了能在易语言上使用因此开发一个通用接口&#xff0c;开发基本思路如下&#xff1a; 可见我们并没有使用什么通信协议或者命令行之类的方法&#xff0c;这种直接封装接口比其他方法更…

TCP 学习笔记

Win R 打开控制台输入CMD 打开小黑窗&#xff0c; 输入ipconfig 查询本机地址 “外网IP是全世界唯一的IP地址,仅分配给一个网络设备。而内网IP是由路由器分配给每一部内部使用的IP地址,而内网的所有用户都是通过同一个外网IP地址进行上网的,而内网的IP地址每个人的都不一样…

(四)WPF - 布局

一、布局过程 WPF 布局包括两个阶段&#xff1a;一个测量阶段和排列阶段 在测量阶段&#xff0c;容器遍历所有子元素&#xff0c;并询问子元素它们所期望的尺寸。在排列阶段&#xff0c;容器在合适的位置放置子元素。&#xff08;每个元素都被其父元素告知它自己的尺寸是多少…

【软件设计师暴击考点】下午题高频考点暴击系列

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;软件…

电脑选购必备的六大技巧

目录 1、CPU方面 2、显卡方面 3、电脑主板方面 4、内存和硬盘方面 5、电脑机箱和电源方面 6、装机过程要全程参与 今天小编给大家分享电脑选购组装必备的六大技巧&#xff0c;希望对大家实际选购、组装电脑提供一些帮助&#xff01; 买电脑要注意哪些问题 1、CPU方面 C…

Qt Model-View架构领悟

1.架构的选择 1.1是否需要委托 模型视图架构图如下所示&#xff0c;模型视图架构源于MVC模式&#xff1a;模型&#xff08;Model&#xff09;是应用对象&#xff0c;表示数据&#xff1b;视图&#xff08;View&#xff09;是模型的用户界面&#xff0c;用以显示数据&#xff…

springboot基础(78):Freemarker模板生成word文档

文章目录 前言如何使用Freemakrer生成word文档1. 制作模板2. 编写工具类 遇到的问题下载失败如何只生成文件不下载 前言 利用Freemarker模板生成word文档。示例&#xff0c;将左侧的模板生成为右侧的文档并下载。 如何使用Freemakrer生成word文档 1. 制作模板 1.编辑一份a…

实现注册与登录(企业级)

目录 实现注册超级管理员功能&#xff08;持久层&#xff09; 一、判定系统是否已经绑定超级管理员 二、编写保存用户记录的代码 三、编写查询用户ID的代码 实现注册超级管理员功能&#xff08;业务层&#xff09; 一、获取OpenId 二、编写注册新用户的业务代码 掌握 R…

tuple 和数组区别

元组&#xff08;tuple&#xff09;和数组&#xff08;array&#xff09;都是 Python 中用于存储多个值的数据结构&#xff0c;但它们在实现和使用上有一些区别。 元组是不可变的&#xff0c;而数组是可变的。即元组一旦创建&#xff0c;其内容就不能被修改&#xff0c;而数组…

高数笔记1(第一章函数 极限 连续 第一节函数第二节极限-极限的概念与性质)

目录 第一章 函数 极限 连续第一节 函数第二节 极限一、极限的概念与性质数列的极限例1例2 函数的极限极限的性质&#xff08;保号性重点 有界性&#xff09;例12例13例14 函数极限与数列极限的关系例15 第一章 函数 极限 连续 第一节 函数 判断有界要用函数的绝对值&#xff…

Toolformer:可以教会自己使用工具的语言模型

Toolformer&#xff1a;可以教会自己使用工具的语言模型 摘要Introduction现有大模型的局限处理办法本文的idea Approach样例化API调用执行API调用筛选API调用模型微调 实验局限 论文地址点这里 摘要 语言模型&#xff08;LMs&#xff09;呈现了令人深刻的仅使用少量的范例或…

2022(一等奖)D1649基于多源卫星遥感的干旱区农作物耗水精细模拟

作品介绍 1 研究背景及目标 1.1 研究区概况 本次研究的研究区位于甘肃省张掖市内。张掖市位于甘肃省西部&#xff0c;河西走廊中段&#xff0c;属干旱和半干旱两种气候类型&#xff0c;其特点是夏季短而酷热&#xff0c;冬季长而严寒&#xff0c;干旱少雨&#xff0c;且降水分…

MySQL-SQL存储过程/触发器详解(下)

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a; 小刘主页 ♥️努力不一定有回报&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️学习两年总结出的运维经验&#xff0c;以及思科模拟器全套网络实验教程。专栏&#xf…