一、问题

人最害怕的事情之一,就是无聊。什么事情会让人无聊?做相同的、单调的、枯燥的事情。写代码一样,对于相同的、类似的东西,来回写,大家都写吐了,大家就产生了无聊,生无可恋的感觉。人是感情的奴,任由着情感代理着生活,指导着工作,支配着行为。本身的无聊,导致我们百无聊赖,岁月蹉跎,年轻的生命,怎么能够被辜负!我们要拒接无聊,拒绝虚度光阴,不写重复相似的代码。

二、问题产生的原因

为什么总是在写重复的代码——至少在感觉上是?

  1. 全局认知不足,无法识别到相通类似的功能或需求,只有头疼医头脚疼医脚。此次开发,前后端人员到位不是同一时间点,同时任务分阶段展开,这样就导致管窥蠡测,只见一斑,后端人员只看后端的一部分,而且也只看当前任务阶段的内容,忽略了后端的全局,也忽略了前端接口交互的全局。比如,最近前端人员老在抱怨,一个上传文件的接口,为什么写那么多,每个地方都有,还不一样,他们都没法公用一个组件。
  2. 任务分配方式导致。前端任务分拆,多以 js 功能拆分;后端任务分解,多以需求模块划分;划分的标准不同,导致抽离的公共组件无法匹配,需要适配,搞到最后,成了不如特殊情况特殊化,个个都单独搞一套。这样就完全违背了组件初衷,代码复用的原则。
  3. 设计的边界模糊,同一件事情的描述不同,存在多个版本的说法。在拿到第一版的需求原型(或者第一次交流原型的内容)时,第一步最会受到原型的引导,甚至诱导,把同一件事剖分为二、为三……这样就无法将抽象的层次建立,导致每一个都成为特立独行的一个。本来初步的认知就不完整,二次、三次的原型交流又发生变化,被迫放弃原有的归纳抽象,重新设计,又考虑到变化的存在,开始倾向于实际问题实际化处理——建立单独的一套使用,以应对每一处的变化(任何变化,都只影响一处)。可是,实际上是原型一处变化,影响处处(修改多处)。
  4. 时间不足。我总是听到相关人员来问进度。进度,也只是关心原型上的功能有没有,不怎么留意代码的好坏。任何代码都是存在优化点的,只是愿不愿意给出时间,愿不愿意下功夫去修改。然而,时间的不足,就否定了一切。

    三、如何避免写无聊的代码

    做错,失败,总是能找到原因的,可是万事俱备只欠东风的时候又有多少,所以,有条件要上,没条件创造条件也要上。不管问题是由多少原因导致,无论外部的还是内部的,是他人的,还是自己的原因,都不重要,我们只是要把事情做好。那么到底怎么避免(减少)重复的代码?

  5. 抽离公共部分,建立抽象类;

  6. 利用设计原则,设计模式,建立起对每个特例业务的处理。
  7. 利用泛型,用于返回值、参数的限定等。

    例子(文件、链接的参数文件为例)

    现在以上传文件(包含链接的)参数文件为例:
    文件上传,按照传入的参数分类,除了文件本身,一个是有参数,一个是无参数。所以抽离出一个文件本身这个参数。同时考虑到,是否需要多文件上传,进一步抽离出文件数组参数。其它参数归类到对应的
    文件上传,对文件的处理。对文件的处理,主要分两步。第一步,记录到MySQL(writeToMysql);第二步,文件存放到minIO(writeToMinIO)。步骤都一样,所以抽离出写入数据库,和存入minIO两个公共抽象方法。writeToMysql,参数自定义;writeToMinIO,主要参数就三个,一个是 bucket,二个是 path,三个是 file;
    文件上传完成后的业务处理,有的需要记录历史版本,有的需要出发其它业务;这个就可以理解为勾子函数,代码执行后的回调等。
    上传的返回参数。

    代码

    ```java package com.hbte.sharp.po.file;

import com.hbte.sharp.common.session.IdGenerator; import com.hbte.sharp.exception.ServiceException; import com.hbte.sharp.util.SpringAppBeanUtil; import com.pandora.bean.FileBean; import com.pandora.core.DocumentClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.multipart.MultipartFile;

import java.io.IOException; import java.io.InputStream; import java.util.Objects;

/**

  • 文件上传抽象类 *
  • @author Bruce
  • @date 2021/1/11 16:04 / public abstract class AbstractUploadFile { private final static Logger LOG = LoggerFactory.getLogger(AbstractUploadFile.class); /*

    • minIO backet (类似 Windows 下的盘符) */ private String bucket;

      /**

    • 文件存放位置(路径,类似Windows 下的目录) */ private String path;

      /**

    • 业务处理 *
    • @param *
    • @return */ public R handle(final P param) { if (Objects.isNull(param)) {

      1. throw new ServiceException("请填写相关的参数。");

      } Object result = writeToMysql(param); if (!Objects.isNull(param.getFile())) {

      1. for (MultipartFile file : param.getFile()) {
      2. IdGenerator idGenerator = SpringAppBeanUtil.getBean("idGenerator", IdGenerator.class);
      3. String originalFilename = file.getOriginalFilename();
      4. int idxOfDot = originalFilename.lastIndexOf(".");
      5. if (idxOfDot > 0) {
      6. originalFilename = originalFilename.substring(0, idxOfDot) + "_"
      7. + idGenerator.nextId()
      8. + originalFilename.substring(idxOfDot);
      9. }
      10. upload(file, originalFilename);
      11. }

      } if (Objects.isNull(result)) {

      1. return null;

      } return (R) result; }

      public F handleAndFeedBack(final P param) { if (Objects.isNull(param)) {

      1. throw new ServiceException("请填写相关的参数。");

      } writeToMysql(param); if (!Objects.isNull(param.getFile())) {

      1. for (MultipartFile file : param.getFile()) {
      2. IdGenerator idGenerator = SpringAppBeanUtil.getBean("idGenerator", IdGenerator.class);
      3. String originalFilename = file.getOriginalFilename();
      4. int idxOfDot = originalFilename.lastIndexOf(".");
      5. if (idxOfDot > 0) {
      6. // 处理名称,防止重名(名称中加入雪花id)
      7. originalFilename = originalFilename.substring(0, idxOfDot) + "_"
      8. + idGenerator.nextId()
      9. + originalFilename.substring(idxOfDot);
      10. } else {
      11. originalFilename = originalFilename + "_" + idGenerator.nextId();
      12. }
      13. upload(file, originalFilename);
      14. }

      } Object feedBack = feedBack(param); if (Objects.isNull(feedBack)) {

      1. return null;

      } return (F) feedBack; }

      /**

    • 文件上传 *
    • @param file 文件对象
    • @param fileName 文件名称(允许自定义名称,但是不验重) */ protected void upload(MultipartFile file, String fileName) { if (Objects.isNull(file)) {

      1. throw new ServiceException("请选择文件");

      } InputStream inputStream = null; try {

      1. DocumentClient documentClient = SpringAppBeanUtil.getBean(DocumentClient.class);
      2. FileBean fileBean = new FileBean();
      3. fileBean.setDirName(bucket);
      4. fileBean.setFilename(path + fileName);
      5. inputStream = file.getInputStream();
      6. documentClient.upload(inputStream, fileBean);

      } catch (IOException e) {

      1. LOG.error("上传文件失败: {}", e);
      2. throw new ServiceException("上传文件失败。");

      } finally {

      1. if (inputStream != null) {
      2. try {
      3. inputStream.close();
      4. } catch (IOException e) {
      5. LOG.error("关闭流失败: {}", e);
      6. throw new ServiceException("上传文件失败:关闭流失败。");
      7. }
      8. }

      } }

      /**

    • 写入数据库 *
    • @param param 参数
    • @param 返回值 *
    • @return */ protected abstract R writeToMysql(final P param);

      /**

    • 回调函数,或者说是一个其他业务处理函数(比如在需求计划中,如果上传方案文件,需要判断是否存在缺失的情况) *
    • @param *
    • @return */ protected F feedBack(final P param) { return null; }

      public String getBucket() { return bucket; }

      public void setBucket(String bucket) { this.bucket = bucket; }

      public String getPath() { return path; }

      public void setPath(String path) { this.path = path; } }

//////////////////////////////////// package com.hbte.sharp.po.file;

import org.springframework.web.multipart.MultipartFile;

/**

  • 文件上传参数 *
  • @author Bruce
  • @date 2021/1/11 16:06 */ public class BaseUploadFileParam { public MultipartFile[] getFile() {

    1. return file;

    }

    public void setFile(MultipartFile[] file) {

    1. this.file = file;

    }

    private MultipartFile[] file; }

///////////////////////////////////////////// package com.hbte.sharp.po.file;

import com.hbte.sharp.mapper.EquipmentFileMapper; import com.hbte.sharp.util.SpringAppBeanUtil;

/**

  • 文件上传 *
  • @author Bruce
  • @date 2021/1/11 18:46 */ public class BaseUploadFile extends AbstractUploadFile { @Override protected Boolean writeToMysql(BaseUploadFileParam param) {
    1. EquipmentFileMapper equipmentFileMapper = SpringAppBeanUtil.getBean(EquipmentFileMapper.class);
    2. return true;
    } }

``` 生产系统文件上传,导致写了多处,一个主要原因就是数据库模型建立不对,将设备与物料的分开了。