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

我在本系列的每个部分中发展的示例是某种“提要处理程序”,用于处理文档。 在上一部分中,我从一些原始代码开始,并应用了一些重构来描述“什么”而不是“如何”。

为了帮助代码向前发展,我们需要先讲一个故事 。 那就是这部分的地方。

如果您是第一次来这里,最好从头开始阅读。 它有助于了解我们从何处开始以及如何在整个系列中继续前进。

这些都是这些部分:

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

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

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

作为参考,我们现在以以下代码为起点:

class FeedHandler {Webservice webserviceDocumentDb documentDbvoid handle(List<Doc> changes) {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)}}
}

大声读出

当我最初开始使用Spock作为测试框架时,由于它是多年前Grails默认提供的,因此它的众多功能和易用性给我留下了深刻的印象(至今仍是)。

你知道什么是假人,存根和间谍吧? Mockito拥有它们,Powermock拥有它们以及基本上所有其他严肃的(单元)测试框架。 Mock的概念并不难掌握(您可以在此处阅读全部内容),但是Spock具有一种特殊的方式来描述其(与预期的)模拟交互。

关于“基于交互的测试”的一章非常出色,它解释了如何使用代码示例来记录这些交互。

“模拟”子章开头为:

模拟是描述(强制)规范下的对象与其协作者之间的交互的行为。 这是一个例子:

def "should send messages to all subscribers"() {when:publisher.send("hello")then:1 * subscriber.receive("hello")1 * subscriber2.receive("hello")
}

如果您不熟悉Spock,Groovy或仅具有上述写作风格,请不要担心!

以上Spock文档的作者也认识到,并不是每个人都会立即理解这里发生的事情。

他们会提供一些建议,并继续提供文档:

大声朗读 :“当发布者发送“ hello”消息时,两个订户都应该只收到一次该消息。”

我的重点是“大声朗读”,因为我认为这很重要。 这里不讨论Spock的更多详细信息,而是我自己在日常编码中要牢记的建议本身。

  • 当我编写一段代码时,我可以大声读出吗?
  • 当其他人读取我的代码时,他/她可以大声读出吗?

这里的“大声” 与音量无关,而是可以用一种简洁易懂的方式描述“这里正在发生什么”。 这使得对代码的推理变得容易。

高级与低级

请考虑以下情形:

在不知名的城市中开车游览了几个小时以找到剧院后,汽车导航系统出现故障后,您最终决定停下来询问方向。

您在行人附近停下。

您:

先生,您碰巧知道如何从这里到达剧院

行人:

  1. 当然,这很容易。 开始了:

  2. 检查窗户以确保您具有良好的可见性。 检查后视镜以确保它们正确对齐,从而为您提供正确的道路视野。

  3. 调整座椅,使双脚舒适地到达两个踏板。

  4. 关闭窗口。

  5. 重置转向灯。

  6. 开始驾驶前,请松开驻车制动器。

  7. 啊,我看你有自动档。 请将变速杆置于“驱动器”中。

  8. 慢慢踩刹车,并监控仪表盘仪表。

  9. 继续前进,提高速度,监控车速表,将RPM保持在2000附近

  10. 大约120码后,在开始转向左车道之前,请先与您的方向灯指示至少两秒钟。

  11. 缓慢将汽车移至另一车道。 稍微将您的手放在方向盘上,以改变车道。 车轮只需要很小的移动即可; 因为大多数现代汽车都装有动力转向系统。 更改车道大约需要一到三秒钟。 减少一点,您做得太快了; 再也没有,你做得太慢了。

  12. 再走X步…

  13. 祝好运!

或者,考虑对话将像这样的替代宇宙:

您:

先生,您是否会知道如何从这里到达剧院?

行人:

  1. 当然,这很容易。 开始了:

  2. 左转,过桥。 在你的右边。

  3. 祝好运!

最后一种情况是轻而易举:明确说明要做什么和去哪里!

但是,第一种情况是细节缠身 -有关驾驶汽车本身的低级细节 -即使我们不希望在现实生活中得到这样的指导,我们仍然会编写这样的软件。

告诉我一些正确的内容。 如果我需要具体信息,我会要求它。

(顺便说一句wikihow.com:如何驾驶汽车,请捐赠上述说明中的一些。如果您确实需要学习驾驶,它有很多资源!)

在正确的级别上讲内容,不仅意味着使用正确命名的类和方法,而且还意味着在它们中使用正确的种类

让我们再次看一下我们的代码:

class FeedHandler {Webservice webserviceDocumentDb documentDbvoid handle(List<Doc> changes) {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)}}
}

故事

我们如何在代码中结合“大声读出”和“高级还是低级”?

我们的单handle方法当前显示为什么样?

  1. 查找type -property等于字符串"important"所有文档。

  2. 呼叫createwebservice与文档,返回的资源。

  3. 如果我们有资源,请将资源的id分配给文档的apiId属性。

  4. 将文档的status属性设置为字符串"processed"

  5. 如果发生异常,请将文档的status属性设置为字符串"failed" 将文档的status属性设置为来自异常的message

  6. 最后,使用documentDb在documentDb上调用update

基本上,这只是重复代码语句!

什么故事,我想告诉取而代之的,是以下情况:

  1. 通过Web服务“创建资源”来处理“重要”文档。

  2. 每次成功时,将两者关联在一起并“将文档标记为已处理”,否则将其标记为“失败”。

读得很好,你不觉得吗?

实际上,我们可以通过在IDE中使用几种“提取方法”重构并为提取的方法选择一些好的名称来实现这一目标。

在上面的故事中,双引号短语是我想在高层看到的重要部分。

“重要”

我为什么要关心文档使用什么属性来确定其重要性? 现在是字符串"important" ,表示“嘿,我很重要!” 但是如果条件变得更加复杂怎么办?

doc.type == 'important'提取到其自身的方法isImportant

changes.findAll { doc -> isImportant(doc) }// ...private boolean isImportant(doc) {doc.type == 'important'}

“创造资源”

我为什么在这里关心如何在Web服务中调用什么方法? 我只想创建一个资源。

将与Web服务的所有事务都提取到它自己的方法(称为createResource

def resource = createResource(doc)// ...private Resource createResource(doc) {webservice.create(doc)}

“更新为已处理”

提取将资源/文档/将状态设置为其自己的方法(称为updateToProcessed

updateToProcessed(doc, resource)// ...private void updateToProcessed(doc, resource) {doc.apiId = resource.iddoc.status = 'processed'}

“更新失败”

不在乎细节。 提取到updateToFailed

updateToFailed(doc, e)// ...private void updateToFailed(doc, e) {doc.status = 'failed'doc.error = e.message}

看来我们最后只剩下了documentDb.update(doc)

这是在数据库中存储已处理/失败文档的一部分,我已经在最高级别上进行了描述。

我将其放在每个刚刚创建的updateTo*方法中-较低的级别。

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)}

那么,提取出细节之后,有什么变化?

void handle(List<Doc> changes) {changes.findAll { doc -> isImportant(doc) }.each { doc ->try {def resource = createResource(doc)updateToProcessed(doc, resource)} catch (e) {updateToFailed(doc, e)}}}

任何人(例如,同事,您未来的自我)都会“一口气”地读一读,将会了解30,000英尺的行程。

如果您需要任何这些步骤的详细信息,只需深入了解该方法即可。

能够写声明式的东西(本系列的前一部分)并在正确的水平上讲故事(本部分)还将有助于在第3部分及以后的部分中更轻松地进行将来的更改。

现在就这样

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

class FeedHandler {Webservice webserviceDocumentDb documentDbvoid handle(List<Doc> changes) {changes.findAll { doc -> isImportant(doc) }.each { doc ->try {def resource = createResource(doc)updateToProcessed(doc, resource)} catch (e) {updateToFailed(doc, e)}}}private 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)}}

翻译自: https://www.javacodegeeks.com/2017/11/functional-java-example-part-2-tell-story.html

功能Java示例 第2部分–讲故事相关推荐

  1. java 示例_功能Java示例 第2部分–讲故事

    java 示例 这是称为" Functional Java by Example"的系列文章的第2部分. 我在本系列的每个部分中开发的示例是某种"提要处理程序" ...

  2. java 函数式编程 示例_功能Java示例 第8部分–更多纯函数

    java 函数式编程 示例 这是第8部分,该系列的最后一部分称为"示例功能Java". 我在本系列的每个部分中开发的示例是某种"提要处理程序",用于处理文档. ...

  3. java 示例_功能Java示例 第5部分–将I / O移到外部

    java 示例 这是称为" Functional Java by Example"的系列文章的第5部分. 在上一部分中,我们停止了对文档的变异,并返回了数据的副本. 现在,我们需要 ...

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

    java 示例 这是称为" Functional Java by Example"的系列文章的第4部分. 在上一部分中,我们讨论了一些副作用,并且我想进一步详细说明如何通过将不可变 ...

  5. java 示例_功能Java示例 第3部分–不要使用异常来控制流程

    java 示例 这是称为" Functional Java by Example"的系列文章的第3部分. 我在本系列的每个部分中开发的示例是某种"提要处理程序" ...

  6. 大数据 java 代码示例_功能Java示例 第7部分–将失败也视为数据

    大数据 java 代码示例 这是称为" Functional Java by Example"的系列文章的第7部分. 我在本系列的每个部分中开发的示例是某种"提要处理程序 ...

  7. 对象作为参数示例java_功能Java示例 第6部分–用作参数

    对象作为参数示例java 这是称为" Functional Java by Example"的系列文章的第6部分. 我在本系列的每个部分中开发的示例是某种"提要处理程序& ...

  8. 功能Java示例 第8部分–更多纯函数

    这是第8部分,该系列的最后一部分称为" Functional Java by Example". 我在本系列的每个部分中发展的示例是某种"提要处理程序",用于处 ...

  9. 功能Java示例 第7部分–将失败也视为数据

    这是称为" Functional Java by Example"的系列文章的第7部分. 我在本系列的每个部分中发展的示例是某种"提要处理程序",用于处理文档. ...

最新文章

  1. 【ACM】杭电OJ 1002
  2. docker 上传到自己的容器
  3. java面试 内存中堆和栈的区别
  4. JQuery EasyUi控件值的获取与设置
  5. damage framework can be used by economics
  6. 机器学习实战 - 读书笔记(04) - 朴素贝叶斯
  7. 云服务已一步一步“入侵”我们生活
  8. 使用vscode连接阿里云服务器报错: ssh: Could not resolve hostname Name or service not known
  9. Flask 区域块简单原理实现
  10. 过期域名如何助力犯罪分子攻破企业防御
  11. 设置Image控件加载图片完毕后的效果.
  12. JS 获取当前页面url(不含参数)
  13. 杰理AD14N/AD15N---串口中断问题
  14. Android——ViewHolder的作用与用法
  15. maven 环境变量的配置
  16. 东南大学破格保研挂科_兜兜转转终入四牌楼——东南大学保研心得
  17. IDM下载器下载百度网盘文件
  18. 深信服校园招聘c/c++软件开发A卷
  19. 如何连接到GBase的XDM服务器
  20. 基于web的教学答疑系统

热门文章

  1. 全文搜索!收藏这篇Solr ElasticSearch 长文就可以搞定
  2. 面象对象设计6大原则之一:单一职责原则
  3. 学习java多线程,这必须搞懂的这几个概念
  4. 【Java】jdk和eclipse下载安装
  5. 《金色梦乡》金句摘抄(十一)
  6. PL/SQL经典练习
  7. 基本数据类型、包装类、String三者之间的相互转换
  8. 中南大学计算机网.doc,中南大学计算机网络实验报告.doc
  9. 转:json与map互转
  10. MySQL日志:binlog、事务日志(redo、undo)