关于提高复杂业务逻辑代码可读性的思考

目录

前言

需求场景

常规写法

拆分方法

领域对象

总结


前言

实际工作中大部分时间都是在写业务逻辑,一般都是三层架构,表示层(Controller)接收客户端请求,并对入参做检验,业务逻辑层(Service)负责处理业务逻辑,一般开发都是在这一层中写具体的业务逻辑。数据访问层(Dao)是直接和数据库交互的,用于查数据给业务逻辑层,或者是将业务逻辑层处理后的数据写入数据库。

简单的增删改查接口不用多说,基本上写好一部分,剩下的都是复制粘贴然后稍微修改一下就行了,有些甚至可以用工具来直接生成。不过,系统中还是会存在一些相对复杂的接口,一般都是系统的核心功能,其中的业务逻辑就复杂多了,代码量少说也得大几百行才能够实现功能,这种代码往往就是“屎山”了。往往后续接手的人在不熟悉整体业务逻辑的情况下,再加上代码量大,代码编写不规范的话,维护起来相当痛苦。

经历过维护“屎山”的痛苦,到自己开发“屎山”,自然希望在自己能够遵循代码规范,开发出一座相对好维护,可读性更高的“屎山”,让自己多积点阴德,多攒点人品。这里分享下写“屎山”代码的心路历程

需求场景

开发一个 api 调用服务,让用户自定义要调用 api 的各种配置,例如 api 的 url 地址,请求方式,query 参数名,header 参数名,body 参数名(表单或者json),api 返参等等,配置完成后,即可提供给其他系统模块进行调用。

api 提供一个调用的接口,其他系统模块调用时按照 api 配置的输入参数名称,传入对应的输入参数值,返回 api 调用模块配置的返参。

调用接口的整体逻辑可以抽象成以下几个步骤

  1. 获取 api 配置信息
  2. 构造请求参数
  3. 构造请求头
  4. 构造请求体
  5. 校验必填字段
  6. 调用 api
  7. 校验 api 返回值
  8. 返回 api 返回值

将业务逻辑分解后,就可以直接开始写代码了。

常规写法

将所有的业务逻辑都放在一个方法中处理。

public class ApiService {

    public Object call(Input input) {
        
        //获取 api 配置信息

        //构造请求参数

        //构造请求头

        //构造请求体

        //校验必填字段

        //调用 api

        //校验 api 返回值

        //返回 api 返回值

    }

}

优点:

  • 业务逻辑分解合理,再加上每一段业务有对应的注释的话,可读性还是很好的。(理想情况下,实际上可能就没有注释,也没有拆分业务逻辑,全都堆在一起了)
  • 业务逻辑都在一个方法里,省去了各种变量的传参,开发一时爽。

缺点:

  • 业务处理复杂导致一个方法代码行数太多了(大几百行甚至上千行),而一个显示屏幕一般也就显示几十行,给看代码的人增加了负担。
  • 代码行数多,无法直接一眼了解整体的业务流程,必须看完整个方法才能了解整个业务流程。
  • 可能出现一个变量横跨几百行的情况,不好维护。

拆分方法

分解业务逻辑后,将每段的业务逻辑都用一个 private 方法封装起来。

public class ApiService {

    public Object call(Input input) {

        //方法的出入参根据实际业务各不相同,这里只是简单表示下拆分方法去分解复杂业务逻辑
        
        Object info = getBaseInfo(param ...);

        Object query = buildQuery(param ...);

        Object header = buildHeader(param ...);

        Object body = buildBody(param ...);

        checkRequiredField(param ...);

        Object result = invoke(param ...);

        checkReturnResult(param ...);

        return result;
    }

    private Object getBaseInfo(Param param ...) { ... }

    private Object buildQuery(Param param ...) { ... }

    private Object buildHeader(Param param ...) { ... }

    private Object buildBody(Param param ...) { ... }

    private boolean checkRequiredField(Param param ...) { ... }

    private Object invoke(Param param ...) { ... }

    private boolean checkReturnResult(Param param ...) { ... }

}

优点:

  • 将业务逻辑拆分到方法后,就能从最外层的方法上一眼了解整个业务逻辑了,而不用去通读整个方法,可读性更好。
  • 大方法拆分成小方法,每个方法职责越小,越好维护。

缺点:

  • 分解业务逻辑到不同的小方法后,不同的方法之间如果需要用到同一个变量,则需要通过入参来传递该变量,业务复杂的情况下,共享变量会越多,方法入参也就变得非常多。
  • Java 的方法只能有一个返回值,但是有可能会出现一个方法需要返回多个值到外层整体方法,然后将返回值作为其他方法的入参,处理起来非常不优雅。
  • 业务越复杂,拆分的 private 方法越多,在整个 Service 类中的占比越大,这些 private 方法对于 Service 类中的其他 public 方法是不相干的,但是却还是在同一个 Service  类下,显得类非常臃肿。

领域对象

为了解决上面所有的痛点,可以抽象出一个专门处理这个业务逻辑的领域对象

public class ApiService {

    public Object call(Input input) {

        //方法的出入参根据实际业务各不相同,这里只是简单表示下拆分方法去分解复杂业务逻辑
        
        ApiDo apiDo = ApiDo.build(input);

        return apiDo.getBaseInfo(param ...)
            .buildQuery()
            .buildHeader()
            .buildBody()
            .checkRequiredFields(param ...)
            .invoke()
            .checkReturnResult(param ...)
            .getResult();
    }

}


public class ApiDo {

    Input input;

    //业务处理中用到的业务变量
    Object field1;
    Object field2;
    Object result;

    public static build(Input input) {
        this.input = input;
    }


    public ApiDo getBaseInfo(param ...) {
        //处理业务逻辑
        return this;
    }

    public ApiDo buildQuery() {
        //处理业务逻辑
        return this;
    }

    public ApiDo buildHeader() {
        //处理业务逻辑
        return this;
    }

    public ApiDo buildBody() {
        //处理业务逻辑
        return this;
    }

    public ApiDo checkRequiredFields(param ...) {
        //处理业务逻辑
        return this;
    }

    public ApiDo invoke() {
        //处理业务逻辑
        return this;
    }

    public ApiDo checkReturnResult(param ...) {
        //处理业务逻辑
        return this;
    }

    public ApiDo getResult() {
        //处理业务逻辑
        return result;
    }

}

使用领域对象,可以同时拥有上面写法的共同优点,同时又可以避免掉它们的缺点,相对来说,更加地面向对象编程。

  • 抽象出了领域对象,使得 Service 层更加精简,不再需要臃肿的 private 方法。
  • Service 层方法就能够一眼了解整体的业务逻辑。
  • 方法的链式调用,天然具有很好的可读性。
  • 可以使用对象的成员变量来保存不同方法共同需要的业务变量,精简方法的入参。

总结

针对复杂的业务逻辑,可以通过抽象出一个领域对象来处理,分解业务逻辑到领域对象的方法中, 使用建造者模式创建领域对象,然后链式调用方法,这样处理,能使代码的可读性更高,也更加地面向对象编程。提高代码可读性,积阴德,攒人品。

你可能感兴趣的:(开发经验,java,数据库,开发语言)