Java 函数式编程思考 —— 授人以渔
引言
最近在使用函数式编程时,突然有了一点心得体会,简单说,用好了函数式编程,可以极大的实现方法调用的解耦,业务逻辑高度内聚,同时减少不必要的分支语句(if-else)。
一、函数式编程就是Lambda表达式吗?
Java语言早在 JDK8 就提供了函数式编程的基础。
你可能会问,函数编程不就是lambda表达式吗?
的确,大多数开发可能还停留在 lambda 表达式的使用层面,但请注意,我从标题、文章开篇都在强调“函数式编程”,很明显,我有意区别函数式编程和lambda表达式两者的概念。
Java 8 引入的函数式编程到底是什么?最近我在开发过程中遇到了一个场景,才让我解开了这个困扰我的问题。
二、一个小场景——多路调用
我遇到的场景其实并不复杂,或者说我们每天都在写如此场景,我甚至并不知道这么简单的场景有没有相应的专有名词来表示,就暂将其称为“多路调用场景”。让我们简单模拟一下。
场景描述:
A Service 和 B Service 都依赖 CService。
A 和 B都用到了 C的一个方法 doProcess(),但 doProcess() 在处理 A 和 B的请求时,又需要拿到 A 或 B 的业务数据。
该如何实现 doProcess() 方法?
@AllArgsConstructor
class AService {private CService cService;public void processData() {List<Object> all = this.getAll();cService.doProcess(all);}private List<Object> getAll() {return Collections.emptyList();}
}@AllArgsConstructor
class BService {private CService cService;public void processData() {List<Object> data = this.queryBDatas();cService.doProcess(data);}private List<Object> queryBDatas() {return new ArrayList<>();}
}class CService {public void doProcess(List<Object> busiData) {// 执行C自己的处理逻辑...busiData.stream().forEach(d -> {System.out.println(d);});}
}
/*** 测试代码*/
public class Test {public static void main(String[] args) {// 实例化服务对象CService cService = new CService();AService aService = new AService(cService);BService bService = new BService(cService);// A -> C 处理请求aService.processData();// B -> C 处理请求bService.processData();}
}
如上代码所示 A、B 的processData 方法都调用C的doProcess 方法,他们都将 doProcess 所需的数据通过参数传递过去。这种实现方式虽然可以成功的适配不同的调用者,但是数据的生成是在调用 doProcess 前,一旦doProcess执行了一些校验逻辑而无法用到这些已经准备好的数据,就可能白白浪费查询资源。
另一种实现是通过循环依赖,将A或B的实例反过来也注入到 C 服务中,在 doProcess 中,需要用到A 或 B 的数据时才去查询。这种实现方式虽然可以实现懒加载,但又引入了另一个问题,就是高耦合性,而且依然需要通过 if-else 判断具体是要执行 A.getAll 还是需要执行 B.queryList,代码冗杂不说,扩展性也很糟糕。
当然,上述代码只是个模型,实际业务可能比这还要复杂。那到底有没有一种,既可以实现懒加载,又高度内聚,不需要循环依赖的实现方式呢?
三、授人以鱼不如授人以渔
请原谅我起了一个这么哗众取宠的小节标题,我后面会解释。
3.1 传统思路的弊端
传统的实现思路,将数据提前准备好传递过去,或使用循环依赖,增加判断条件,执行不同的业务逻辑。
似乎这类实现已被大家习以为常,但就像前面描述的,数据传递可能会造成性能、资源等浪费;传统的延迟处理又需要搭配循环依赖,从而造成严重的依赖混乱问题,明明公共服务被业务服务依赖,公共服务却反过来还要依赖业务服务,扩展性极低。
而函数式编程可以很好的解决这个问题!
3.2 授人以渔
Java 8 引入的函数式编程,允许开发者将函数像参数一样传递。
直到最近,我才终于理解了这个定义。函数,实际上就是一个具体的处理逻辑,一个解决方案,一个“捕鱼的方法”、一个可开箱即用的“锦囊妙计”。
我们将函数传递到另一个方法中,那么在这个方法中,就可以直接去执行这个函数。
再回到上面的场景中,A和 B调用 C 的 doProcess() 方法,如果使用函数式编程,该如何实现?
首先定义一个业务数据查询函数:
// 业务数据查询函数
@FunctionalInterface
public interface BusiDataQueryFunc {List<Object> queryList();
}
然后将其作为参数声明到 doProcess 参数中,令A或B调用时传递一个具体的实现逻辑,如下所示:
@AllArgsConstructor
class AService {private CService cService;public void processData() {cService.doProcess(() -> this.getAll());}private List<Object> getAll() {// A 业务数据查询逻辑return Collections.emptyList();}
}@AllArgsConstructor
class BService {private CService cService;public void processData() {// 传入数据查询函数cService.doProcess(() -> this.queryBDatas());}private List<Object> queryBDatas() {// B 业务数据查询逻辑return new ArrayList<>();}
}class CService {public void doProcess(BusiDataQueryFunc busiDataQueryFunc) {// 查询调用者所需数据List<Object> busiData = busiDataQueryFunc.queryList();// 执行C自己的处理逻辑...busiData.stream().forEach(d -> {System.out.println(d);});}
}
如此,在doProcess中,就不需要使用任何的 if 判断,同时也实现了懒加载获取到业务的数据。相比传统的将数据传递,或通过特定参数加if-else按条件查询,耦合度更低,这是集性能强、扩展性强、耦合度低等优点于一身的优秀实现方式。
如果把数据传递到方法中,比作授人以鱼,那么函数传递,就是授人以渔。将“捕鱼的方法”告诉被调用者,这就是为什么我将函数式编程称为——授人以渔的开发思想。
四、总结
在多路调用的场景中,通常会需要在被调用方法中使用到调用者的一些数据,传统的编程方式是直接将数据作为参数传递过去,或者通过一些业务标识用if-else的方式来判断该调用哪个业务方法。
直接传递数据的方式,提前将数据准备好,会有性能问题,可能在被调用方法的校验逻辑执行中断,用不到数据,浪费系统资源;而通过普通的if-else 分支,又需要将调用者注入到被依赖方,虽然实现了懒加载,但本身形成了循环依赖,造成了高耦合,存在潜在的开发成本。
所以,Java 8 提供的函数式编程,将获取数据的方式通过函数传递给被调用者,授人以渔,即满足懒加载,又解耦了依赖关系。这在依赖关系复杂的系统中是一个非常有用的设计思想。
相关文章:
Java 函数式编程思考 —— 授人以渔
引言 最近在使用函数式编程时,突然有了一点心得体会,简单说,用好了函数式编程,可以极大的实现方法调用的解耦,业务逻辑高度内聚,同时减少不必要的分支语句(if-else)。 一、函数式编…...
操作系统权限提升(二十八)之数据库提权-SQL Server 数据库安装
SQL Server 数据库安装 SQL Server介绍 SQL Server 是Microsoft 公司推出的关系型数据库管理系统。具有使用方便可伸缩性好与相关软件集成程度高等优点,可跨越从运行Microsoft Windows 98 的膝上型电脑到运行Microsoft Windows 2012 的大型多处理器的服务器等多种平台使用。…...
腾讯mini项目-【指标监控服务重构-会议记录】2023-08-18
2023-08-18 会议纪要 进度 venus 的 metrics 独立分支开发venus 的 trace 修复了一些bug 返回 error 主动调用 span.end() profile 的 watemill pub/sub 和 trace 上报还原原本功能profile 的 hyperscan 的继续调研 待办 调研如何关闭otel,设置开关配置性能benc…...
如何通过axios拦截器,给除了登录请求以外,axios的所有异步请求添加JWT令牌!
在 Vue 项目中配置除了登录请求以外的所有请求的令牌,通常涉及到在请求头中添加令牌(Token)信息。这可以通过使用 Axios 或其他 HTTP 请求库来实现。以下是一般的步骤: 1. **安装 Axios**: 如果你还没有安装 Axios&a…...
Spring学习笔记9 SpringIOC注解式开发
Spring学习笔记8 Bean的循环依赖问题_biubiubiu0706的博客-CSDN博客 注解的存在主要是为了简化XML的配置.Spring6倡导全注解式开发 回顾下 注解怎么定义,注解中的属性怎么定义 注解怎么使用 通过反射机制怎么读取注解 注解的自定义 注解的使用 通过反射机制怎么读取注解 I…...
【新日标习题集】第13課 までのまとめ (discarded)
2. 学校にコンピューターがごだいあります。 这个句子好像有点问题,辞典中没有查到有「ごだい」这个单词 学校里有5台电脑。 5. わたしは英語がよくわかります。 我很懂英语。...
Java基础常考知识点(基础、集合、异常、JVM)
作者:逍遥Sean 简介:一个主修Java的Web网站\游戏服务器后端开发者 主页:https://blog.csdn.net/Ureliable 觉得博主文章不错的话,可以三连支持一下~ 如有需要我的支持,请私信或评论留言! Java基础常考知识点…...
虚拟机桥接模式下没有无线网卡选项
我以为是雷电模拟器占用了网卡的缘故,但想起之前可能修改了无线网卡的某些内容,于是到网络属性里面查看。 如下所示,原来是之前我不小心把这个红箭头指向的项目取消勾选了。...
设计模式笔记
关于设计模式 1. 如何阅读本文 略 2. 面向对象程序设计简介 2.1 面向对象程序设计基础 面向对象程序设计 (Object-Oriented Programming,缩写为 OOP)是一种范式,其基本理念是将 数据块 及 与数据相关的行为 封装成为特殊的、…...
c==ubuntu+vscode debug redis7源码
新建.vscode文件夹,创建launch.json和tasks.json {"version": "0.2.0","configurations": [{"name": "C/C Launch","type": "cppdbg","request": "launch","prog…...
java字符串储存底层原理
字符串原理:原理1: 内存原理 (1)直接赋值给字符串,会把这个字符串放到常量池里,如果之后出现重复使用这个字符串的,就会直接从这个常量池中去引用,不会再去new一个字符串 (2)new出来的字符串不会重复使用,而是开辟一个新的空间存储原理2: 字符串中的""比较的是什么?…...
c++获取当前时间的字符串
代码 void getNowTimePrefix(std::string& prefix) {std::time_t nowTime;struct tm* p new tm;std::time(&nowTime);localtime_s(p, &nowTime);int year p->tm_year 1900;int month p->tm_mon 1;int day p->tm_mday;int hour p->tm_hour;int …...
【精品】通用Mapper 批量更新bug解决方案
问题描述 环境:mysql8.xmybatis3.5.13tk.mybatis4.2.3 在使用tk.mybatis做批量更新时,程序会报错,说是执行的SQL语法错误,经研究源代码发现tk.mybatis在实现批量更新时是通过多次执行update语句实现的。这本身就不符合MySQL批量…...
腾讯mini项目-【指标监控服务重构-会议记录】2023-07-06
7/6 会议记录 Profile4个步骤 解压kafka消息初始化性能事件,分析事件将数据写入kafkaRun 开始执行各stage handler 上报耗时到otel-collector。。。 // ConsumerDispatchHandler consumer // // param msg *sarama.ConsumerMessage // param consumer *databus.K…...
【React】函数式组件和类式组件的用法和逻辑
组件的使用 当应用是以多组件的方式实现,这个应用就是一个组件化的应用 注意: 组件名必须是首字母大写虚拟DOM元素只能有一个根元素虚拟DOM元素必须有结束标签 < /> 渲染类组件标签的基本流程React 内部会创建组件实例对象调用render()得到虚拟 …...
题目 1061: 二级C语言-计负均正
从键盘输入任意20个整型数,统计其中的负数个数并求所有正数的平均值。 保留两位小数 样例输入 1 2 3 4 5 6 7 8 9 10 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 样例输出 10 5.50 解题思路: 如题所示,输入20个正负数,---》求付数的个…...
数位和(C++)
系列文章目录 进阶的卡莎C++_睡觉觉觉得的博客-CSDN博客数1的个数_睡觉觉觉得的博客-CSDN博客双精度浮点数的输入输出_睡觉觉觉得的博客-CSDN博客足球联赛积分_睡觉觉觉得的博客-CSDN博客大减价(一级)_睡觉觉觉得的博客-CSDN博客小写字母的判断_睡觉觉觉得的博客-CSDN博客纸币(…...
[牛客复盘] 牛客周赛round13 20230924
[牛客复盘] 牛客周赛round13 20230924 总结矩阵转置置2. 思路分析3. 代码实现 小红买基金1. 题目描述2. 思路分析3. 代码实现 小红的密码修改1. 题目描述2. 思路分析3. 代码实现 小红的转账设置方式1. 题目描述2. 思路分析3. 代码实现 小红打boss1. 题目描述2. 思路分析3. 代码…...
mybatsi-MyBatis的逆向工程
mybatsi-MyBatis的逆向工程 一、前言二、创建逆向工程的步骤1.添加依赖和插件2.创建MyBatis的核心配置文件3.创建逆向工程的配置文件4.执行MBG插件的generate目标 一、前言 正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。 Hibernate是支…...
转转闲鱼交易猫链接源码 支持二维码收款
最新仿二手闲置链接源码 后台一键生成链接,后台管理教程:解压源码,修改数据库config/Congig 不会可以看源码里有教程 下载程序:https://pan.baidu.com/s/16lN3gvRIZm7pqhvVMYYecQ?pwd6zw3...
Python爬虫基础(三):使用Selenium动态加载网页
文章目录 系列文章索引一、Selenium简介1、什么是selenium?2、为什么使用selenium3、安装selenium(1)谷歌浏览器驱动下载安装(2)安装selenium 二、Selenium使用1、简单使用2、元素定位3、获取元素信息4、交互 三、Phan…...
Linux系统下安装Mysql
1、执行命令:rpm -qa | grep -i mysql,先查看系统之前是否有安装相关的rpm包,如果有,会显示类似下面的信息; 2、通过命令yum -y remove mysql-* 一次性删除系统上所有相关的rpm包,或者通过命令yum -y …...
Jenkins学习笔记1
CI 服务器: 认识Jenkins: Jenkins是一个可扩展的持续集成(CI)引擎,是一个开源项目,旨在提供一个开放易用的软件平台,使得软件持续集成变成可能。Jenkins非常易于安装和配置,简单易…...
注意力机制
概念没什么好说的,反正大家都会说,具体实战怎么写才是最为重要的 1.自注意力 假设有一组数据,都是一维的向量,这个向量可能是一个样本,可能是其他什么,都无所谓。 假设有一组一维向量x1,x2,x3,x4,x5; 第…...
JVM-Java字节码技术笔记
Java字节码技术 Java字节码是java代码编译后的中间代码格式,JVM需要读取并解析字节码才能执行相应的任务 获取字节码简介:由单字节(byte)的指令组成 操作码( 指令), 主要由类型前缀和操作名称两部分组成。根据指令的性质…...
C++ 友元、重载、继承、多态
友元 关键字:friend 友元的三种实现 全局函数做友元类做友元成员函数做友元 全局函数做友元 //建筑物类 class Building {//goodGay全局函数是Building好朋友,可以访问Building中私有成员friend void goodGay(Building& building); public:Build…...
Spring Boot 日志文件
前言 本篇博客主要介绍自定义的日志打印、日志的级别高低、如何保存日志等等..... 一、日志是什么?日志有什么用? 日志就是我们控制台上输出的内容,控制台上的输出的信息就是日志信息,如下所示: 日志有什么用&#x…...
vulhub venom
文章目录 靶场环境信息收集ftp服务二、信息利用三、任意文件上传三 sudo提权靶场环境 `vmware 靶场信息:https://www.vulnhub.com/entry/venom-1,701/ 下载地址:https://download.vulnhub.com/venom/venom.zip 新建虚拟机打开下载后的ovf文件 遇见导入失败合规性检查时,重试…...
量化交易之One Piece篇 - linux - 定时任务(重启服务器、执行程序、验证)
linux 执行命令: crontab -e 0 5 * * 1-5 sudo /sbin/shutdown -r now 0 17 * * 1-5 sudo /sbin/shutdown -r now 45 8 * * 1-5 cd /home/ubuntu/onepiece/bin/datacore && ./datacore 45 20 * * 1-5 cd /home/ubuntu/onepiece/bin/datacore && ./datacore 以…...
Qt5开发及实例V2.0-第二十三章-Qt-多功能文档查看器实例
Qt5开发及实例V2.0-第二十三章-Qt-多功能文档查看器实例 第23章 多功能文档查看器实例23.1. 简介23.2. 界面与程序框架设计23.2.1. 图片资源23.2.2. 网页资源23.2.3. 测试用文件 23.3 主程序代码框架23.4 浏览网页功能实现23.4.1 实现HtmIHandler处理器 23.5. 部分代码实现23.5…...
网站备案链接直接查看/全球搜索网站排名
JsDoc 如果你在写javascript,是否羡慕过C,JAVA的文档自动生成工具?是否希望自己的程序也能自动生成一份对应的文档,犹如java API文档一样呢?不要再羡慕了。jsdoc_toolkit.zip 一款强大的js doc生成工具已经能完成你所羡…...
做视频赚钱的网站有哪些/灰色seo关键词排名
问:怎么看CPU的位数? 答:使用例如EVEREST、鲁大师等此类软件能够检测到,以下以我电脑的截图来分析一下: 以上表示既支持32位系统,又支持64位系统; 问:我们说的32位操作系统指的是什么…...
宝塔建站详细教程/今日竞彩足球最新比赛结果查询
大家好,今天我们学习【马上学tensorflow 2.0】之 神经网络的“齿轮”:张量运算。我们 马上学三点 ,张量运算介绍张量点积和几何意义深度学习几何意义大家可以点击下面的“ 了解更多 ”,或搜索“ 马上学123 ”,在线观看…...
wordpress 自定义模版/艾滋病多长时间能查出来
5.26——工作记录前言过程总结重点前言 目标:1.准备查询的基础sql 2.查询页面, 3.点击设备编码按钮的显示 4.图片 过程 deviceStatusPerceptionEventInfoCommon.jsp <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" &…...
品牌创建的六个步骤/seo联盟
决定了学习linux socket programming!! 每天上来更新一下学习的状态! 最近一些事情烦恼,不能再这么颓废下去了,人生没有几年可以蹉跎的!!...
wordpress段间距/肇庆百度快速排名
GCC的安装: 切换到安装光盘目录下: #mount /dev/cdrom /mnt/cdrom #cd /mnt/cdrom 安装GCC依赖的*rpm程序,必须按照顺序依次执行: #rpm -ivh kernel-headers-2.6.18-164.el5.i386.rpm #rpm -ivh compat-glibc-headers-2.3.4-2…...