吸积效应:为什么接口会越来越臃肿?我们从一个接口说起

2023-12-03 15:04

本文主要是介绍吸积效应:为什么接口会越来越臃肿?我们从一个接口说起,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

欢迎大家关注公众号「JAVA前线」查看更多精彩分享文章,主要包括源码分析、实际应用、架构思维、职场分享、产品思考等等,同时欢迎大家加我微信「java_front」一起交流学习


1 从一个接口说起

1.1 初始接口

假设现在有一个创建订单接口:

public OrderCreateResultDTO createOrder(OrderCreateDTO order)
  • 创建订单对象
public class OrderCreateDTO {private String bizColumnA;private String bizColumnB;private String bizColumnC;private String bizColumnD;
}
  • 订单响应对象
public class OrderCreateResultDTO {private String orderId;private String bizColumnA;private String bizColumnB;private String bizColumnC;private String bizColumnD;
}

1.2 发生变化

现在业务发生变化,产品经理提出需求:创建订单时不需要C和D字段,新增E和F字段。如果你是开发人员,你会怎么设计这个接口?有一种比较朴素的思路是直接删除C和D字段,新增E和F字段:

  • 创建订单对象
public class OrderCreateDTO {private String bizColumnA;private String bizColumnB;private String bizColumnE;private String bizColumnF;
}
  • 订单响应对象
public class OrderCreateResultDTO {private String orderId;private String bizColumnA;private String bizColumnB;private String bizColumnE;private String bizColumnF;
}

1.3 发现问题

我们想一想上述方案是否可行呢?答案是不行,有以下三方面原因:

原因一是接口契约性:接口是本服务暴露给外部使用的,相当于上下游签了合同,如果随意修改合同,那么合同严肃性荡然无存。

原因二是版本兼容性:我们知道APP是有版本号的,假设APP版本1使用的是初始接口,这时APP版本2由于新业务需要使用新接口,但是你不能直接把初始接口改的面目全非,因为APP版本1有很多用户在用,如果只考虑新版本,那老版本将会报错。

原因三是时间窗口:我们知道APP发布版本是需要审核的,即使这一版本是强更,也会有一个时间窗口新功能和老功能是并存的,所以服务端接口在升级是必须考虑这种情况。


2 如何思考

既然不能直接升级,那么应该如何解决这个问题?我认为需要从两个维度思考:

  • 分层维度
  • 分端维度

2.1 分层思考

在代码结构落地实践中可以分为多层,但是核心还是三层,我们分别分析每一层如何适配接口变更:

  • 数据层
  • 业务层
  • 表现层

2.1.1 数据层

因为要考虑新老版本并存的问题,所以数据层必须保留新老版本所有字段,标记老字段标记为废弃,但是需要修改字段是否必填性,因为一些老版本字段变成不必填:

public class OrderCreateDO {private String orderId;private String bizColumnA;private String bizColumnB;@Deprecatedprivate String bizColumnC;@Deprecatedprivate String bizColumnD;private String bizColumnE;private String bizColumnF;
}

2.1.2 业务层

业务层是承载核心业务的层级,所以包含大量的业务逻辑,有两种方案:

  • 方案一:新增业务对象,重写业务方法
  • 方案二:修改业务对象,适配业务方法

方案一优点是不与老业务耦合,缺点是新老方法逻辑可能只有少部分不一样,所以需要复制老业务方法大量逻辑到新方法中。

方法二优点是可以复用老逻辑,缺点是新老逻辑耦合,如果适配逻辑没有处理好,可能会影响老逻辑。

所以两种方案有各自使用场景,方案一适用于业务逻辑重大变动场景,既然是重构所以可以重新声明新业务对象:

public class OrderCreateNewBO {private String bizColumnA;private String bizColumnB;private String bizColumnE;private String bizColumnF;
}

方案二适用于业务逻辑微调变动场景,所以老业务对象需要包含新老字段,标记老字段标记为废弃:

public class OrderCreateBO {private String bizColumnA;private String bizColumnB;@Deprecatedprivate String bizColumnC;@Deprecatedprivate String bizColumnD;private String bizColumnE;private String bizColumnF;
}

2.1.3 展示层

展示层不应该处理复杂业务逻辑,而应该是对业务对象的裁剪和适配,所以可以新增一个新版本接口,没有业务层那种负担:

  • 创建订单对象
public class OrderCreateDTOV2 {private String bizColumnA;private String bizColumnB;private String bizColumnE;private String bizColumnF;
}
  • 订单响应对象
public class OrderCreateResultDTOV2 {private String orderId;private String bizColumnA;private String bizColumnB;private String bizColumnE;private String bizColumnF;
}

但是如果展示层不规范,包含大量业务逻辑,那么思考方式和业务层一样,也需要使用方案二。


2.2 分端思考

一个系统在业务上通常分三个端:

  • 面向B端用户
  • 面向C端用户
  • 面向运营用户
    变化多端_1.jpg

这三个端都具有BFF层,但是通常实现技术不同:

  • 面向B端和C端有APP端
  • 面向运营端通常H5实现

所以如果不存在APP端,为了接口不要越来越臃肿,所以面向运营用户BFF层可以考虑删除老字段。


3 技术系统为什么复杂

《为什么需要生物学思维》这本书中提到复杂系统形成的四个原因:

  • 吸积
  • 交互
  • 必须处理的意外情况
  • 普遍的稀有事务

3.1 什么是吸积效应

从字面上解读可以将这一过程理解为吸附和积累。如同一粒粒沙子,每次加入一点点,最终汇聚成一座沙山。在软件开发的世界中,无论每一次的代码迭代在表面上看起来多么独立和微不足道,它们都在客观上促使代码量不断增长。


3.2 吸积效应带来哪些挑战

吸积效应导致代码变得如此庞大和复杂,以至于没有人能够全面掌握这个系统。当系统出现问题或故障时,最熟悉这部分代码的人可能早已离职,消失在人海中。

面对这种情况开发人员往往只能添加一段段兼容逻辑,以期降低对原有系统的影响。然而这种做法反而加剧吸积效应。甚至在某些无奈的情况下,团队可能被迫选择容忍某些错误,因为修复这些错误的代价远大于容忍它们所带来的风险。这就是吸积效应在软件开发中带来的巨大挑战。


3.3 怎么应对

技术系统都是往熵增方向发展,从有序发展到无序,所以工程师要通过一些手段减缓这个进程,常见方案是:

  • 统一技术架构
  • 统一技术规范
  • 统一业务语言
  • 代码分享与审查
  • 技术与业务分享
  • 系统稳定性建设

欢迎大家关注公众号「JAVA前线」查看更多精彩分享文章,主要包括源码分析、实际应用、架构思维、职场分享、产品思考等等,同时欢迎大家加我微信「java_front」一起交流学习

这篇关于吸积效应:为什么接口会越来越臃肿?我们从一个接口说起的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/449873

相关文章

go中空接口的具体使用

《go中空接口的具体使用》空接口是一种特殊的接口类型,它不包含任何方法,本文主要介绍了go中空接口的具体使用,具有一定的参考价值,感兴趣的可以了解一下... 目录接口-空接口1. 什么是空接口?2. 如何使用空接口?第一,第二,第三,3. 空接口几个要注意的坑坑1:坑2:坑3:接口-空接口1. 什么是空接

如何用java对接微信小程序下单后的发货接口

《如何用java对接微信小程序下单后的发货接口》:本文主要介绍在微信小程序后台实现发货通知的步骤,包括获取Access_token、使用RestTemplate调用发货接口、处理AccessTok... 目录配置参数 调用代码获取Access_token调用发货的接口类注意点总结配置参数 首先需要获取Ac

讯飞webapi语音识别接口调用示例代码(python)

《讯飞webapi语音识别接口调用示例代码(python)》:本文主要介绍如何使用Python3调用讯飞WebAPI语音识别接口,重点解决了在处理语音识别结果时判断是否为最后一帧的问题,通过运行代... 目录前言一、环境二、引入库三、代码实例四、运行结果五、总结前言基于python3 讯飞webAPI语音

MyBatis-Plus中Service接口的lambdaUpdate用法及实例分析

《MyBatis-Plus中Service接口的lambdaUpdate用法及实例分析》本文将详细讲解MyBatis-Plus中的lambdaUpdate用法,并提供丰富的案例来帮助读者更好地理解和应... 目录深入探索MyBATis-Plus中Service接口的lambdaUpdate用法及示例案例背景

Java8需要知道的4个函数式接口简单教程

《Java8需要知道的4个函数式接口简单教程》:本文主要介绍Java8中引入的函数式接口,包括Consumer、Supplier、Predicate和Function,以及它们的用法和特点,文中... 目录什么是函数是接口?Consumer接口定义核心特点注意事项常见用法1.基本用法2.结合andThen链

Deepseek R1模型本地化部署+API接口调用详细教程(释放AI生产力)

《DeepseekR1模型本地化部署+API接口调用详细教程(释放AI生产力)》本文介绍了本地部署DeepSeekR1模型和通过API调用将其集成到VSCode中的过程,作者详细步骤展示了如何下载和... 目录前言一、deepseek R1模型与chatGPT o1系列模型对比二、本地部署步骤1.安装oll

MyBatis-Flex BaseMapper的接口基本用法小结

《MyBatis-FlexBaseMapper的接口基本用法小结》本文主要介绍了MyBatis-FlexBaseMapper的接口基本用法小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具... 目录MyBATis-Flex简单介绍特性基础方法INSERT① insert② insertSelec

Spring排序机制之接口与注解的使用方法

《Spring排序机制之接口与注解的使用方法》本文介绍了Spring中多种排序机制,包括Ordered接口、PriorityOrdered接口、@Order注解和@Priority注解,提供了详细示例... 目录一、Spring 排序的需求场景二、Spring 中的排序机制1、Ordered 接口2、Pri

Idea实现接口的方法上无法添加@Override注解的解决方案

《Idea实现接口的方法上无法添加@Override注解的解决方案》文章介绍了在IDEA中实现接口方法时无法添加@Override注解的问题及其解决方法,主要步骤包括更改项目结构中的Languagel... 目录Idea实现接China编程口的方法上无法添加@javascriptOverride注解错误原因解决方

Java function函数式接口的使用方法与实例

《Javafunction函数式接口的使用方法与实例》:本文主要介绍Javafunction函数式接口的使用方法与实例,函数式接口如一支未完成的诗篇,用Lambda表达式作韵脚,将代码的机械美感... 目录引言-当代码遇见诗性一、函数式接口的生物学解构1.1 函数式接口的基因密码1.2 六大核心接口的形态学