功能Java示例 第4部分–首选不变性

这是称为“ Functional Java by Example”的系列文章的第4部分。

在上一部分中,我们讨论了一些副作用,并且我想进一步详细说明如何通过不可变性引入代码中来防止以意外的方式操纵数据。

如果您是第一次来,最好是从头开始阅读。

它有助于了解我们从何处开始以及如何在整个系列中继续前进。

这些都是这些部分:

  • 第1部分–从命令式到声明式
  • 第2部分–讲故事
  • 第3部分–不要使用异常来控制流程
  • 第4部分–首选不变性
  • 第5部分–将I / O移到外部
  • 第6部分–用作参数
  • 第7部分–将失败也视为数据
  • 第8部分–更多纯函数

我将在每篇文章发表时更新链接。 如果您通过内容联合组织来阅读本文,请查看我博客上的原始文章。

每次代码也被推送到这个GitHub项目 。

纯功能

关于我们之前讨论的内容的小结。

  • 函数式编程鼓励使用无副作用的方法(或:函数),以使代码更易于理解且更易于推理 。 如果某个方法仅接受某些输入并每次都返回相同的输出(这使其成为一个函数),则各种优化都可以在后台进行,例如通过编译器或缓存,并行化等。
  • 我们可以再次用函数(计算出的值)替换函数,这称为参考透明度 。

这是上一部分重构后当前的内容:

class FeedHandler {Webservice webserviceDocumentDb documentDbvoid handle(List<Doc> changes) {changes.findAll { doc -> isImportant(doc) }.each { doc ->createResource(doc).thenAccept { resource ->updateToProcessed(doc, resource)}.exceptionally { e ->updateToFailed(doc, e)}}}private CompletableFuture<Resource> createResource(doc) {webservice.create(doc)}private boolean isImportant(doc) {doc.type == 'important'}private void updateToProcessed(doc, resource) {doc.apiId = resource.iddoc.status = 'processed'documentDb.update(doc)}private void updateToFailed(doc, e) {doc.status = 'failed'doc.error = e.messagedocumentDb.update(doc)}}

我们的updateToProcessedupdateToFailed是“不纯的”-它们都将更新现有文档in 。 从Java的返回类型void可以看出,这意味着:什么都不会出来 。 下沉Kong。

private void updateToProcessed(doc, resource) {doc.apiId = resource.iddoc.status = 'processed'documentDb.update(doc)
}private void updateToFailed(doc, e) {doc.status = 'failed'doc.error = e.messagedocumentDb.update(doc)
}

这些类型的方法都围绕您的典型代码库。 因此,随着代码库的增长,在将数据传递给这些方法之一之后,往往很难对数据的状态进行推理。

请考虑以下情形:

def newDocs = [new Doc(title: 'Groovy', status: 'new'),new Doc(title: 'Ruby', status: 'new')
]feedHandler.handle(newDocs)println "My new docs: " + newDocs
// My new docs: 
// [Doc(title: Groovy, status: processed),
//  Doc(title: Ruby, status: processed)]
// WHAT? My new documents aren't that 'new' anymore

罪魁祸首一直在破坏我文件的地位; 首先,它们是“新的”,其次不是。 那不行! 一定是该死的FeedHandler。 谁创作的东西? 为什么会触碰我的数据?

考虑另一种情况,即有多个参与者处理您的业务。

def favoriteDocs = [new Doc(title: 'Haskell'),new Doc(title: 'OCaml'),new Doc(title: 'Scala')
]archiver.backup(favoriteDocs)feedHandler.handle(favoriteDocs)mangleService.update(favoriteDocs)userDao.merge(favoriteDocs, true)println "My favorites: " + favoriteDocs
// My favorites: []
// WHAT? Empty collection? Where are my favorites????

我们从一组项目开始,然后在4种方法中发现我们的数据不见了。

在每个人都可以改变任何事物的世界中,很难在任何给定时间推断任何状态。

它本身甚至还不是“全局状态”,任何拥有(引用到)数据的人都可以清除传递给方法的集合,并可以更改变量。

首选不变性

那是什么 如果对象在实例化后不更改其状态,则该对象是不可变的。

看起来合理吧?

不变性 图片来源: 应对并适应持续变化

关于如何使用您的特定语言进行处理,这里有大量资源。 例如,Java默认不支持不变性。 必须做些工作。

如果有第三方在处理过程中发生问题并更改数据(例如清除我的收藏夹),则可以通过将我的收藏夹传递到不可修改的包装中来快速清除麻烦制造者,例如

def data = [...
]// somewhere inside 3rd-party code
data.clear()// back in my code:
// data is empty *snif*

预防故障:

def data = Collections.unmodifiableCollection([])// somewhere inside 3rd-party code
data.clear() // HAHAA, throws UnsupportedOperationException

在您自己的代码库中,我们可以通过最小化可变数据结构来防止意外的副作用(例如,在某处更改我的数据)。

在大多数FP语言中,例如Haskell , OCaml和Scala ,默认情况下 ,语言本身会促进不变性 。 虽然不是真正的FP语言,但使用ES6编写不可变JavaScript也趋于成为一种好习惯。

首先只读

使用到目前为止所学的原理,并努力防止意外的副作用,我们希望确保实例化实例后, 不能Doc类进行任何更改 –甚至不包括updateToProcessed / updateToFailed方法。

这是我们当前的课程:

class Doc {String title, type, apiId, status, error
}

Groovy不需要进行使Java类变为不可变的所有手动工作,而是借助Immutable -annotation进行了抢救。

当放置在类上时,Groovy编译器进行了一些增强,因此创建后再也没有人可以更新其状态。

@Immutable
class Doc {String title, type, apiId, status, error
}

该对象实际上变为“只读”,并且任何尝试更新属性的操作都将导致恰当命名的ReadOnlyPropertyException

private void updateToProcessed(doc, resource) {doc.apiId = resource.id // BOOM! // throws groovy.lang.ReadOnlyPropertyException: //  Cannot set readonly property: apiId...
}private void updateToFailed(doc, e) {doc.status = 'failed' // BOOM! // throws groovy.lang.ReadOnlyPropertyException: //  Cannot set readonly property: status...
}

但是,等等,这是否意味着updateToProcessed / updateToFailed方法实际上将无法将文档status更新为“已处理”或“失败”?

吉普,这就是不变性带给我们的。 如何修复逻辑?

复制第二

Haskell关于“不可变数据”的指南为我们提供了如何进行操作的建议:

纯功能程序通常在不可变数据上运行。 代替更改现有值,而是创建更改的副本并保留原始副本。 由于结构的未更改部分无法修改,因此它们通常可以在旧副本和新副本之间共享,从而节省了内存。

答:我们克隆它!

我们没有更新的原始数据,我们应该做的一个副本-原来不是我们的,应保持不变。 我们的Immutable -annotation支持一个名为copyWith的参数。

@Immutable(copyWith = true)
class Doc {String title, type, apiId, status, error
}

因此,我们将更改方法以制作状态更改 (以及api id和错误消息) 的原始副本,返回此副本

(总是返回Groovy方法中的最后一条语句,不需要显式的return关键字)

private Doc setToProcessed(doc, resource) {doc.copyWith(status: 'processed',apiId: resource.id)
}private Doc setToFailed(doc, e) {doc.copyWith(status: 'failed',error: e.message)
}

数据库逻辑也已上移,将返回的副本存储起来。

我们已经控制了我们的状态!

现在就这样

如果您以Java程序员的身份担心过多的对象实例化对性能的影响,请在此处发表一篇令人放心的文章 。

作为参考,这是重构代码的完整版本。

class FeedHandler {Webservice webserviceDocumentDb documentDbvoid handle(List<Doc> changes) {changes.findAll { doc -> isImportant(doc) }.each { doc ->createResource(doc).thenAccept { resource ->documentDb.update(setToProcessed(doc, resource))}.exceptionally { e ->documentDb.update(setToFailed(doc, e))}}}private CompletableFuture<Resource> createResource(doc) {webservice.create(doc)}private boolean isImportant(doc) {doc.type == 'important'}private Doc setToProcessed(doc, resource) {doc.copyWith(status: 'processed',apiId: resource.id)}private Doc setToFailed(doc, e) {doc.copyWith(status: 'failed',error: e.message)}}

翻译自: https://www.javacodegeeks.com/2018/06/functional-java-part-4-immutability.html

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

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

相关文章

C语言实用算法系列之学生管理系统_对整个结构体操作_选择排序_提取排序规则

代码 #define _CRT_SECURE_NO_WARNINGS#include <stdio.h> #include <string.h> #include <stdlib.h> void Save();struct SUser {int nNumb;char sName[20];float fMath; }g_user[100];void Print() {puts("\n学号\t姓名\t数学");int i 0;while…

Altium Designer19(AD19)

微信公众号&#xff1a;创享日记 发送关键词&#xff1a;Altium 免费获取Altium Designer19 1、解压文件 2、解压后打开安装文件&#xff08;AD19 setup.exe&#xff09; 3、点击next 4、选择中文并接受协议 5、选择功能&#xff0c;Next 6、选择文件保存路径与安装路径&…

仿真proteus8.7安装

前些天发现了十分不错的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;没有广告&#xff0c;分享给大家&#xff0c;大家可以自行看看。&#xff08;点击跳转人工智能学习资料&#xff09; 微信公众号&#xff1a;创享日记 发送关键词&#xff1a;prot…

C语言实用算法系列之学生管理系统_对整个结构体操作_冒泡排序_提取排序规则

代码 #define _CRT_SECURE_NO_WARNINGS#include <stdio.h> #include <string.h> #include <stdlib.h> void Save();struct SUser {int nNumb;char sName[20];float fMath; }g_user[100];void Print() {puts("\n学号\t姓名\t数学");int i 0;while…

servlet 异常处理_Servlet异常和错误处理示例教程

servlet 异常处理有时候我写了一篇有关Java异常处理的文章&#xff0c;但是当涉及到Web应用程序时&#xff0c;我们需要的不仅仅是Java中的异常处理。 Servlet异常 如果您注意到&#xff0c;doGet&#xff08;&#xff09;和doPost&#xff08;&#xff09;方法将抛出ServletE…

基于51单片机直流电机PWM控制器设计

前些天发现了十分不错的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;没有广告&#xff0c;分享给大家&#xff0c;大家可以自行看看。&#xff08;点击跳转人工智能学习资料&#xff09; 由STC89C52单片机、LCD1602液晶显示屏、霍尔测速传感器、3V直…

大林算法控制仿真实验(计控实验六simulink)

前些天发现了十分不错的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;没有广告&#xff0c;分享给大家&#xff0c;大家可以自行看看。&#xff08;点击跳转人工智能学习资料&#xff09; 微信公众号&#xff1a;创享日记 发送关键词&#xff1a;计控…

振铃的消除仿真实验(计控实验七simulink)

前些天发现了十分不错的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;没有广告&#xff0c;分享给大家&#xff0c;大家可以自行看看。&#xff08;点击跳转人工智能学习资料&#xff09; 微信公众号&#xff1a;创享日记 发送关键词&#xff1a;计控…

基于触摸屏PLC的温度采集及简单控制

微信公众号&#xff1a;创享日记 发送关键词&#xff1a;plc4 免费获取完整无水印实验报告及源文件 一、实验目的 1、掌握模拟量输入输出的编写方法&#xff1b; 2、了解触摸屏画面编辑及组态的方法。 二、实验设备 三、实验步骤 1、如图所示&#xff0c;编辑触摸屏画面并定义…

OSI模型七层

OSI将计算机网络体系结构(architecture&#xff09;划分为以下七层&#xff1a; 一、应用层&#xff08;快递物品本身&#xff09; 网络服务接口&#xff0c;定义程序间通信标准&#xff0c;应用层协议&#xff08;HTTP…&#xff09; 二、表示层&#xff08;打包&#xff09…

内存heap_哪个内存更快?Heap或ByteBuffer或Direct?

内存heapJava正在成为新的C / C &#xff0c;它被广泛用于开发高性能系统。 对像我这样的数百万Java开发人员来说非常好&#xff01; 在这个博客中&#xff0c;我将分享我可以用Java完成的不同类型的内存分配的实验&#xff0c;以及从中获得什么好处。 Java中的内存分配 Java…

Java EE 8的前5个新功能

备受期待的Java Enterprise Edition 8版本拥有两个激动人心的新API&#xff08;JSON绑定1.0和Java EE Security 1.0&#xff09;以及对当前API的改进&#xff08;JAX-RS 2.1&#xff0c;Bean Validation 2.0&#xff0c;JSF 2.3&#xff0c;CDI 2.0&#xff0c;JSON-P&#xff…

C语言实用算法系列之二级指针用法简介

一、几个知识点 内存四区&#xff1a;栈、全局&#xff08;静态&#xff09;&#xff0c;常量区&#xff0c;除此以外剩余的空间暂时不能随意使用&#xff1b;除此以外剩余的空间只要通过malloc函数申请一下&#xff0c;就可以使用了&#xff1b;申请一个堆上的单个int变量的方…

C语言实用算法系列之行指针

代码 #include <stdio.h>void Test(double (*a)[3]) {printf("sizeof(a)%d\n", sizeof(a));printf("sizeof(*a)%d\n", sizeof(*a));printf("sizeof(a[1])%d\n", sizeof(a[1])); }void main() {double ar[2][3] { {1.0,2.1,3.2},{4.3,5.4…

C语言实用算法系列之DOS传参“加减乘除计算器”

简介 主要采用str族函数实现字符检测&#xff0c;只能用DOS传参进行计算&#xff0c;详见运行结果。 代码 #include <stdio.h> #include <string.h> #include <stdlib.h>/* int main(int argc, char** argv) { int i0; printf("总共有%d条有效字符串…

ARP地址解析协议(深信服X计划)

文章目录一、ARP需求背景二、ARP概述及工作原理三、免费ARP概述及案例四、代理ARP概述及案例一、ARP需求背景 在以太网中&#xff0c;一个主机和另一个主机进行直接通信&#xff0c;必须要知道目标主机的MAC地址。单这个目标MAC地址是如何获得的呢&#xff1f;它就是通过ARP&a…

grails框架_Play和Grails Java框架的优缺点

grails框架框架通过为程序员提供一些有用的功能来简化应用程序开发过程。 由于开发人员的普遍使用&#xff0c;Java框架经常被开发人员使用。 您可以在市场上找到各种Java开发框架。 新手开发人员经常在论坛上发布一个常见问题&#xff1a;“哪种Java框架是最好的&#xff1f;”…

TCP和UDP协议(深信服X计划)

文章目录一、TCP协议概述二、TCP三次握手和四次挥手三、UDP协议概述四、TCP和UDP对比及应用场景一、TCP协议概述 TCP (Transmission Control Protocol传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议&#xff0c;由ETF的RFC 793定义。在简化的计算机网络O…

C语言实用算法系列之时间族函数、目录遍历

时间族函数测试 代码 #define _CRT_SECURE_NO_WARNINGS#include <stdio.h> #include <stdlib.h> #include <time.h>void main() {time_t tt;//long __int64time(&tt);tm* time localtime(&tt);char* ws[] { "日","一","…

跟踪反应流–将Spring Cloud Sleuth与Boot 2结合使用

Spring Cloud Sleuth在OpenZipkin Brave的基础上增加了对Spring工具的支持&#xff0c; 从而使Spring Boot应用程序的分布式跟踪变得异常简单。 这是一篇关于如何使用此出色的库添加对分布式跟踪支持的简要说明。 考虑两个应用程序–一个使用上游服务应用程序的客户端应用程序…