做简约头像网站,网站建设属于商标哪个类,泛华建设集团有限公司网站,海东网站建设这是称为“ Functional Java by Example”的系列文章的第4部分。 在上一部分中#xff0c;我们讨论了一些副作用#xff0c;并且我想进一步详细说明如何通过将不可变性引入代码中来防止以意外的方式操纵数据。 如果您是第一次来#xff0c;最好是从头开始阅读。 它有助于… 这是称为“ Functional Java by Example”的系列文章的第4部分。 在上一部分中我们讨论了一些副作用并且我想进一步详细说明如何通过将不可变性引入代码中来防止以意外的方式操纵数据。 如果您是第一次来最好是从头开始阅读。 它有助于了解我们从何处开始以及如何在整个系列中继续前进。 这些都是这些部分 第1部分–从命令式到声明式 第2部分–讲故事 第3部分–不要使用异常来控制流程 第4部分–首选不变性 第5部分–将I / O移到外部 第6部分–用作参数 第7部分–将失败也视为数据 第8部分–更多纯函数 我将在每篇文章发表时更新链接。 如果您通过内容联合组织来阅读本文请查看我博客上的原始文章。 每次代码也被推送到这个GitHub项目 。 纯功能 关于我们之前讨论的内容的小结。 函数式编程鼓励使用无副作用的方法或函数以使代码更易于理解且更易于推理 。 如果某个方法仅接受某些输入并每次都返回相同的输出这使其成为一个纯函数则各种优化都可以在后台进行例如通过编译器或缓存并行化等。 我们可以再次用纯函数计算出的值替换纯函数这称为参考透明度 。 这是上一部分重构后当前的内容 class FeedHandler {Webservice webserviceDocumentDb documentDbvoid handle(ListDoc changes) {changes.findAll { doc - isImportant(doc) }.each { doc -createResource(doc).thenAccept { resource -updateToProcessed(doc, resource)}.exceptionally { e -updateToFailed(doc, e)}}}private CompletableFutureResource createResource(doc) {webservice.create(doc)}private boolean isImportant(doc) {doc.type important}private void updateToProcessed(doc, resource) {doc.apiId resource.iddoc.status processeddocumentDb.update(doc)}private void updateToFailed(doc, e) {doc.status faileddoc.error e.messagedocumentDb.update(doc)}} 我们的updateToProcessed和updateToFailed是“不纯的”-它们都将更新现有文档in 。 从Java的返回类型void可以看出这意味着什么都不会出来 。 下沉Kong。 private void updateToProcessed(doc, resource) {doc.apiId resource.iddoc.status processeddocumentDb.update(doc)
}private void updateToFailed(doc, e) {doc.status faileddoc.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 arent 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(ListDoc 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 CompletableFutureResource 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