java 函数式编程 示例
功能编程(FP)的目的是避免重新分配变量,避免可变的数据结构,避免状态并全程支持函数。 如果将功能性技术应用于日常Java代码,我们可以从FP中学到什么?
在这个名为“ Functional Java by Example”的系列文章中,我将分8步重构现有代码,以查看是否可以用Java达到Functional Nirvana 。
我对Haskell或F#等“真正的”功能语言没有太多经验,但是我希望在每篇文章中以示例方式演示将这些实践中的一些应用于每天的Java代码意味着什么。
希望最后您获得了一些见识,并且知道选择一些有益于您自己的代码库的技术。
这些都是这些部分:
- 第1部分–从命令式到声明式
- 第2部分–命名事物
- 第3部分–不要使用异常来控制流程
- 第4部分–首选不变性
- 第5部分–将I / O移到外部
- 第6部分–用作参数
- 第7部分–将失败也视为数据
- 第8部分–更多纯函数
我将在每篇文章发表时更新链接。 如果您通过内容联合组织来阅读本文,请查看我博客上的原始文章。
每次代码也被推送到这个GitHub项目 。
免责声明:代码是用Apache的Groovy中 ,主要是为简洁,所以我不必键入的东西(你知道:打字),其中不要紧的例子。 第二,这门语言只是让我开心。
为什么要关心函数式编程(FP)?
如果您不是在时髦的实时流数据事件处理框架上执行Haskell,F#或Scala,则最好打包。 这些天,甚至JavaScript的人都在围绕您的方法旋转函数-这种语言已经存在了一段时间。
那里有很多文章和视频,使您相信,如果这些天没有跳上功能性潮流,那么您会被旧的OOP束缚所抛弃,坦率地说,它们会在几年内过时。
好吧,我在这里告诉您这不是完全正确的,但是FP 确实有一些前提,例如可读性,可测试性和可维护性 ,我们也在我们的(企业)Java代码中力求实现的值正确吗?
在阅读本文时,多年以来,您可能已经对FP是前进还是后退或2017-2018年年期失败表达了相同的直率观点,您只是愿意接受新想法
通过学习FP,您可以提高每种语言的技能。
确定自己是什么,你可以从中学到如何自己编程可以从中受益。
如果您能胜任这项任务,那么让我们从...开始
现有的一些代码
关于示例代码的一句话:为这样的博客提供人为的示例是非常棘手的:它应该足够容易吸引广泛的受众,应该足够简单,无需太多上下文就可以理解,但仍然足够有趣以至于导致理想的学习效果。
展望未来,本系列的每一期将以前一期为基础。 下面是我们将作为起点的代码。
因此,戴上眼镜,看看您是否熟悉下面的编码样式。
class FeedHandler {Webservice webserviceDocumentDb documentDbvoid handle(List<Doc> changes) {for (int i = 0; i < changes.size(); i++) {def doc = changes[i]if (doc.type == 'important') {try {def resource = webservice.create(doc)doc.apiId = resource.iddoc.status = 'processed'} catch (e) {doc.status = 'failed'doc.error = e.message}documentDb.update(doc)}}}
}
- 这是某种
FeedHandler
。 - 它有两个属性,一些
Webservice
类和DocumentDb
类。 - 有一个
handle
方法可以对Doc
对象列表进行处理。 文件?
尝试弄清楚这里发生了什么
..
..
..
做完了吗
读这样的东西有时会使您感到自己像一个解析器。
扫描类名( FeedHandler?
)和一个方法( void handle
)可以使您感到有些FeedHandler?
,从而使您感到FeedHandler?
。
但是,弄清楚在handle
方法中确切“处理”了什么要困难得多。
- 那里有一个
for-loop
-但是到底是在迭代什么? 多少次? - 调用此变量
webservice
,返回称为resource
。 - 如果
webservice
成功返回,则正在迭代的doc
(文档?)将更新为状态。 - 似乎
webservice
还可以抛出一个Exception
,它被捕获和文档与其他状态更新。 - 最终,该文档被此
documentDb
实例“更新”。 看起来像一个数据库。 - 等等,这仅适用于“重要”文档 -在进行上述所有操作之前,首先检查
doc.type
。
也许,您听说过以下短语:
读取的代码多于编写的代码。
看看这块美丽:
for (int i = 0; i < changes.size(); i++) {
上面的代码以命令式的方式编写,这意味着操作状态和行为的具体语句被明确地写出。
- 用零初始化一个
int i
- 当
int i
小于changes
列表的大小时循环 - 每次迭代以1递增
int i
用这种命令式 (过程)编码风格(大多数主流语言,包括Java,C ++,C#等面向对象编程(OOP)语言,都被设计为主要支持),开发人员编写了计算机所需的确切语句。执行以完成特定任务。
一些非常命令性 (过程性)代码的信号:
- 专注于如何执行任务
- 状态更改和执行顺序很重要
- 许多循环和条件
该代码明确地侧重于“如何”,这使“什么”难以确定。
专注于什么
就像本文的标题已经不一样,我们的第一步是从命令式的编码和重构方法转变为更具声明性的样式-FP是一种形式。
这个循环最困扰我。
这是代码的新版本。
class FeedHandler {Webservice webserviceDocumentDb documentDbvoid handle(List<Doc> changes) {// for (int i = 0; i < changes.size(); i++) {// def doc = changes[i]changes.findAll { doc -> doc.type == 'important' }.each { doc ->try {def resource = webservice.create(doc)doc.apiId = resource.iddoc.status = 'processed'} catch (e) {doc.status = 'failed'doc.error = e.message}documentDb.update(doc)}}
}
有什么变化?
-
if (doc.type == 'important')
部分已替换为findAll { doc -> doc.type == 'important' }
if (doc.type == 'important')
findAll { doc -> doc.type == 'important' }
findAll { doc -> doc.type == 'important' }
再次涉及文档集合本身- 意思是“查找所有重要的文档,并仅返回那些重要文档的新集合” - 强制性的
for-loop
(带有中间的i
变量)已由文档集合本身上的声明性的each
方法所取代- 表示“为列表中的每个文档执行一段代码,我不在乎您如何执行” &#55357;&#56898;
不用担心each
和findAll
:这些方法是Groovy所添加的,我将它们与Java在同一代码库中愉快地一起使用,添加到了任何Set(例如Set,List,Map)中。 Vanilla Java 8具有等效的机制,例如forEach
可以更声明性地迭代集合。
导致可读软件的原因是:
描述“什么”而不是“如何” 。
如果我以更加实用的方式编写代码,就可以轻松地看到发生了什么,这可以节省我的时间 (因为是的,我90%的时间都在读取代码而不是编写代码),并且这样编写代码不容易出错 ,因为更少的行会减少隐藏错误的机会。
现在就这样
在第2部分中,我们将正确命名事物 ,为更多功能的编程铺平道路,例如在本系列的后续版本中,“ Either”或“ Try”。
翻译自: https://www.javacodegeeks.com/2017/11/functional-java-example-part-1-imperative-declarative.html
java 函数式编程 示例