java 7.函数-递归_带有谓词的Java中的函数样式-第2部分

java 7.函数-递归

在本文的第一部分中,我们介绍了谓词,这些谓词通过具有返回true或false的单一方法的简单接口,为Java等面向对象的语言带来了函数式编程的某些好处。 在第二部分和最后一部分中,我们将介绍一些更高级的概念,以使您的谓词发挥最大作用。
测试中
谓词发光的一种明显情况是测试。 每当您需要测试将遍历数据结构和某些条件逻辑混合在一起的方法时,通过使用谓词,您可以隔离地测试每一半,首先遍历数据结构,然后遍历条件逻辑。
第一步,您只需将始终为true或始终为false谓词传递给该方法即可摆脱条件逻辑,而只专注于对数据结构的正确处理:
// check with the always-true predicate
final Iterable<PurchaseOrder> all = orders.selectOrders(Predicates.<PurchaseOrder> alwaysTrue());
assertEquals(2, Iterables.size(all));// check with the always-false predicate
assertTrue(Iterables.isEmpty(orders.selectOrders(Predicates.<PurchaseOrder> alwaysFalse())));
在第二步中,您只需分别测试每个可能的谓词。
final CustomerPredicate isForCustomer1 = new CustomerPredicate(CUSTOMER_1);
assertTrue(isForCustomer1.apply(ORDER_1)); // ORDER_1 is for CUSTOMER_1
assertFalse(isForCustomer1.apply(ORDER_2)); // ORDER_2 is for CUSTOMER_2
这个例子很简单,但是您可以理解。 为了测试更复杂的逻辑,如果测试功能的每半部分还不够,则可以创建模拟谓词,例如,一次返回true的谓词,然后始终返回false。 由于严格分开关注点,因此强制这样的谓词可能会大大简化您的测试设置。
谓词对于测试非常有用,以至于如果您倾向于做一些TDD,我的意思是说,如果您可以通过测试的方式影响您的设计方式,那么一旦您知道谓词,它们肯定会在设计中找到自己的方式。
向团队解释
在我从事的项目中,团队最初并不熟悉谓词。 但是,这个概念很简单,也很有趣,每个人都可以快速上手。 事实上,令我惊讶的是,谓词的概念如何从我编写的代码自然传播到我的同事的代码中,而没有太多的福音。 我想谓词的好处不言而喻。 从诸如Apache或Google之类的知名公司那里获得成熟的API,也有助于说服它是严肃的东西。 现在有了功能性编程炒作,它应该更容易出售!
简单优化
通常的优化是使谓词尽可能不变无状态 ,以使它们的共享无需考虑线程。 这样就可以在整个过程中使用一个实例(作为一个实例,例如作为静态最终常量)。 如果需要,可以在运行时缓存在编译时无法枚举的最常用谓词。 与往常一样,仅当您的探查器报告确实需要它时,才执行此操作。
可能的话,谓词对象可以在其构造函数中(自然是线程安全的)或惰性地预先计算其评估所涉及的一些计算。
谓词应该是无副作用的 ,换句话说就是“只读”:谓词的执行不应引起系统状态的任何可观察到的变化。 一些谓词必须具有某种内部状态,例如用于分页的基于计数器的谓词,但它们仍不得更改其所应用系统中的任何状态。 在内部状态下,它们也无法共享,但是,如果它们支持每次连续使用之间的重置,则可以在其线程内重用它们。
细粒度的界面:谓词的受众范围更大
在大型应用程序中,您会发现自己为完全不同的类型编写了非常相似的谓词,但它们具有与客户相关的共同属性。 例如,在管理页面中,您可能希望按客户过滤日志; 在CRM页面中,您要按客户过滤投诉。
对于每个此类X,您都需要另一个CustomerXPredicate来按客户对其进行过滤。 但是由于每个X都以某种方式与客户相关,所以我们可以使用一种方法将其(在Eclipse中提取接口)分解为CustomerSpecific接口:
public interface CustomerSpecific {Customer getCustomer();
}
这个细粒度的界面让我想起了某些语言的特质 ,但它没有可重用的实现。 它也可以看作是在静态类型语言中引入动态类型的一种方法,因为它可以使用getCustomer ()方法以不同的方式调用任何对象。 当然,我们的类PurchaseOrder现在实现了此接口。
一旦有了该接口CustomerSpecific ,就可以在其上定义谓词,而不是像以前那样在每个特定类型上定义谓词。 这有助于在整个大型项目中利用几个谓词。 在这种情况下,谓词CustomerPredicate与操作它的CustomerSpecific接口位于同一位置,并且具有通用类型CustomerSpecific
public final class CustomerPredicate implements Predicate<CustomerSpecific>, CustomerSpecific {private final Customer customer;// valued constructor omitted for claritypublic Customer getCustomer() {return customer;}public boolean apply(CustomerSpecific specific) {return specific.getCustomer().equals(customer);}
}
请注意,该谓词本身可以实现CustomerSpecific接口,因此甚至可以对其进行评估!
当使用类似特征的接口时,您必须注意泛型并稍微更改类PurchaseOrders中需要Predicate <PurchaseOrder>的方法,以便它也可以接受PurchaseOrder超类型的任何谓词:
public Iterable<PurchaseOrder> selectOrders(Predicate<? super PurchaseOrder> condition) {return Iterables.filter(orders, condition);
}
域驱动设计规范
埃里克·埃文斯(Eric Evans)和马丁·福勒(Martin Fowler)一起编写了规范Specification ,这显然是一个谓词。 实际上,“谓词”一词是逻辑编程中使用的词,并且编写了规范说明用于解释我们如何将逻辑编程的某些能力借用到面向对象的语言中。
在《域驱动设计》一书中,埃里克·埃文斯(Eric Evans)详细介绍了这种模式,并给出了几个规范的示例,这些示例均表示域的各个部分。 就像本书描述的策略模式一样,当将策略模式应用于域时,仅是策略模式,在某种意义上,规范模式也可以被视为专用于领域方面的谓词版本,其另外的目的是清楚地标记和标识该域。商业规则。
作为说明,Specification模式中建议的方法名称为: isSatisfiedBy(T):boolean ,它重点关注域约束。 正如我们之前用谓词所见,封装在Specification对象中的业务逻辑原子可以使用布尔逻辑(或(而不是全部)逻辑)重新组合,就像在Interpreter模式中一样 。
该书还介绍了一些更高级的技术,例如查询数据库或存储库时的优化以及使用。  
查询时的优化
以下是优化技巧,但我不确定您是否会需要它们。 但这确实是事实,谓词在过滤数据集时相当笨拙:必须仅对集合中的每个元素进行评估,这可能会导致大型集合的性能问题。 如果将元素存储在数据库中并提供了谓词,则通过谓词检索每个元素以逐个过滤它们对于大型集合来说似乎并不是一个正确的主意……
当遇到性能问题时,您可以启动分析器并找到瓶颈。 现在,如果经常调用谓词从数据结构中过滤出元素是一个瓶颈,那么如何解决呢?
一种方法是摆脱完整的谓词,然后回到硬编码,更易于出错,重复且测试较少的代码。 只要我能找到更好的替代方法来优化谓词,我就会一直反对这种方法,并且有很多选择。
首先,深入了解代码的使用方式。 本着领域驱动设计的精神,每当出现问题时,就应该系统地寻找领域的洞察力。
通常,系统中有明确的使用模式。 尽管统计,它们为优化提供了巨大的机会。 例如,在我们的PurchaseOrders类中,检索每个PENDING订单的频率可能比其他情况要高得多,因为在我们的虚构示例中,从业务的角度来看,这样做才有意义。
朋友共谋
根据使用模式,您可以编写专门针对其优化的替代实现。 在我们经常查询待处理订单的示例中,我们将编写一个替代实现FastPurchaseOrder ,该实现使用一些预先计算的数据结构来使待处理订单准备就绪以便快速访问。
现在,为了从这种替代实现中受益,您可能很想更改其接口以添加专用方法,例如selectPendingOrders() 。 请记住,在您只有通用的selectOrders(Predicate)方法之前。 添加额外的方法在某些情况下可能还可以,但是可能会引起一些问题:您还必须在其他所有实现中都实现此额外的方法,并且额外的方法可能对特定的用例而言过于具体,因此可能不适用于接口。
通过仅期望谓词的完全相同的方法使用内部优化的技巧只是使实现认识到它所关联的谓词。 我称其为“ Friend Complicity ”,是指C ++中的friend关键字。
/** Optimization method: pre-computed list of pending orders */
private Iterable<PurchaseOrder> selectPendingOrders() {// ... optimized stuff...
}public Iterable<PurchaseOrder> selectOrders(Predicate<? super PurchaseOrder> condition) {// internal complicity here: recognize friend class to enable optimizationif (condition instanceof PendingOrderPredicate) {return selectPendingOrders();// faster way}// otherwise, back to the usual casereturn Iterables.filter(orders, condition);
}
显然,它增加了两个实现类之间的耦合,否则它们应该彼此忽略。 同样,如果直接给«friend»谓词,且周围没有装饰器或合成器,则它只会对性能有所帮助。
Friend Complicity真正重要的是确保方法的行为永不妥协,无论是否进行优化,都必须始终满足接口的约定,只是性能改进可能会或不会发生。 另外请记住,您可能有一天可能要切换回未经优化的实施。  
SQL受损
如果订单实际上存储在数据库中,则可以使用SQL快速查询它们。 顺便说一句,您可能已经注意到谓词的概念恰好是您在SQL查询中的WHERE子句之后放置的。
仍然使用谓词并提高性能的第一种简单方法是使某些谓词实现附加接口SqlAware ,方法为asSQL():String ,该字符串返回与谓词本身的评估相对应的确切SQL查询 。 当对数据库支持的存储库使用谓词时,存储库将调用此方法,而不是通常的validate(Predicate)apply(Predicate)方法,然后使用返回的查询来查询数据库。
我称这种方法是SQL受损的,因为谓词现在已被特定于数据库的详细信息所污染,因此应该更经常地忽略它。
直接使用SQL的替代方法包括使用存储过程命名查询 :谓词必须提供查询的名称及其所有参数。 在存储库和传递给它的谓词之间进行双调度也是一种选择:存储库在其附加方法selectElements(this)上调用谓词,该方法本身又调用正确的预选择方法findByState(state):存储库中的Collection ; 谓词然后对返回的集合应用其自己的过滤,并返回最终的过滤集合。
包容性
包含是一个逻辑概念,用于表达一个概念包含另一个概念的关系,例如“红色,绿色和黄色包含在术语颜色下”( Merriam-Webster )。 谓词之间的包含可能是在代码中实现的非常强大的概念。
让我们以广播股票报价的应用程序为例。 注册时,我们必须声明我们有兴趣观察哪些报价。 我们可以通过简单地传递一个只对我们感兴趣的股票评估为真的股票谓词来做到这一点:
public final class StockPredicate implements Predicate<String> {private final Set<String> tickers;// Constructors omitted for claritypublic boolean apply(String ticker) {return tickers.contains(ticker);}}
现在,我们假设该应用程序已经广播了有关消息传递主题的标准的流行行情设置,并且每个主题都有自己的谓词。 如果它可以检测到我们要使用的谓词是“包括”,或包含在标准谓词之一中,则可以订阅它并保存计算。 在我们的案例中,通过简单地在谓词上添加其他方法,这种包含就相当容易了:
public boolean encompasses(StockPredicate predicate) {return tickers.containsAll(predicate.tickers);}

包含是关于评估“包含”的另一个谓词 。 当谓词基于示例(如示例)或基于数字或日期的间隔时,这很容易。 否则,您可能不得不诉诸类似于Friend Complicity的技巧,即以个案的方式识别另一个谓词,以决定是否包含该谓词。

总体而言,请记住,一般情况下很难实现包容,但是即使部分包容也可能非常有价值,因此它是工具箱中的重要工具。
结论
谓词很有趣,并且可以增强您的代码和您的思考方式!
干杯,
该部分的单个源文件可下载cyriux_predicates_part2.zip(固定的损坏链接)
参考: 带有谓词的纯Java语言中的函数式风格– Cyrille Martraire博客博客中来自JCG合作伙伴 Cyrille Martraire的第二部分 。

翻译自: https://www.javacodegeeks.com/2012/05/functional-style-in-java-with_23.html

java 7.函数-递归

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

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

相关文章

apk改之理_一份礼物.apk-O泡果奶的逆向分析

事情起因是震惊全国大学生的1013事件&#xff01;&#xff01;&#xff01;刚好看到社团群里在讨论这个&#xff0c;于是就发挥专业特长分析一下拿到apk ,第一步肯定先放到虚拟机里跑一下看下效果emmm这似曾相识的页面,这熟悉的音量,唯一变化的就是音乐变成了O泡果奶的魔性洗脑…

《java从入门到精通》pdf

下载地址&#xff1a; 网盘下载 内容简介 编辑本书从初学者角度出发&#xff0c;通过通俗易懂的语言、丰富多彩的实例&#xff0c;详细介绍了使用Java语言进行程序开发应该掌握的各方面技术。全书共分28章&#xff0c;包括&#xff1a;初识Java&#xff0c;熟悉Eclipse开发工具…

接口测试php代码,简易 PHP API 测试

简易 PHP API 测试PHP代码/*Author : JamesDate : 2020/3/21Versions: Beta v2HEPL :HTTP POST :1.{ "select": "mock_data", "where": [{ "id": "1" },{ "username": "james" }], "limit": …

哪个线程执行CompletableFuture的任务和回调?

尽管CompletableFuture大约是两年前&#xff08;&#xff01;&#xff09;于2014年3月在Java 8中引入的&#xff0c;但它仍然是一个相对较新的概念。但是&#xff0c;此类不是很广为人知是一件好事&#xff0c;因为它很容易被滥用&#xff0c;尤其是在线程和线程方面。一路涉及…

时区处理总结

我司业务分布在跨时区的多个国家&#xff0c;我在日常积累了较多的时区处理经验&#xff0c;在此分享一下 首先基本概念&#xff0c;时间分为2种 datetime&#xff0c;这是给人读的时间&#xff0c;分时区。如2000-1-1 12:00:00 gmttimestamp&#xff0c;这是unix时间戳&#x…

python彩色螺旋线_解决python彩色螺旋线绘制引发的问题

彩色螺旋线的绘制代码如下&#xff1a; import turtle import time turtle.pensize(2) turtle.bgcolor(black) colors [red, yellow, purple, blue] turtle.tracer(False) for x in range(400): turtle.forward(2*x) turtle.color(colors[x % 4]) turtle.left(91) turtle.trac…

matlab中的logspace,matlab中的logspace(a,b,n)究竟怎么理解,看下例题,谁能详细解给我看...

matlab中的logspace(a&#xff0c;b&#xff0c;n)究竟怎么理解&#xff0c;看下例题&#xff0c;谁能详细解给我看来源:互联网 宽屏版 评论2009-12-07 10:11:30分类: 教育/科学 >> 学习帮助问题描述:vec2logspace(0,10,6)的答案为什么是1.0e010 * 0.0000 0.0000 0.000…

ID3和C4.5分类决策树算法 - 数据挖掘算法(7)

&#xff08;2017-05-18 银河统计&#xff09;决策树(Decision Tree&#xff09;是在已知各种情况发生概率的基础上&#xff0c;通过构成决策树来判断其可行性的决策分析方法&#xff0c;是直观运用概率分析的一种图解法。由于这种决策分支画成图形很像一棵树的枝干&#xff0c…

mysql 聚簇索引和非聚簇索引_图文并茂,说说MySQL索引

点击上方 小伟后端笔记 &#xff0c;选择 星标 公众号重磅资讯、干货&#xff0c;第一时间送达作者&#xff1a;小小木的博客来源&#xff1a;cnblogs.com/wyc1994666/p/10831039.html序开门见山&#xff0c;直接上图&#xff0c;下面的思维导图即是现在要讲的内容&#xff0c;…

mysql数据库日志截断,MySQL基础(十一):查询截取分析

下面是小凰凰的简介&#xff0c;看下吧&#xff01;&#x1f497;人生态度&#xff1a;珍惜时间&#xff0c;渴望学习&#xff0c;热爱音乐&#xff0c;把握命运&#xff0c;享受生活&#x1f497;学习技能&#xff1a;网络 -> 云计算运维 -> python全栈( 当前正在学习中…

component是什么接口_阿里高级技术专家:整洁的应用架构“长”什么样?

很多同学不止一次和我反馈&#xff0c;我们的系统很混乱&#xff0c;主要表现在&#xff1a;应用的层次结构混乱&#xff1a;不知道应用应该如何分层、应该包含哪些组件、组件之间的关系是什么&#xff1b;缺少规范的指导和约束&#xff1a;新加一段业务逻辑不知道放在什么地方…

php把1拆分成三份,【php】位运算如何拆分

举例&#xff0c;比如说下面可以组合成71|2|4; // 7那么比如我给一个15&#xff0c;怎么拆分成&#xff1a;1、2、4、8&#xff0c;或者是任意其他数&#xff0c;怎么进行拆分回答不熟悉php。可以先确定这个数占多少位&#xff0c;比如64或者32等。然后再左移位&#xff0c;按位…

20155202 实验四 Android开发基础

20155202 实验四 Android开发基础 实验内容 1.基于Android Studio开发简单的Android应用并部署测试; 2.了解Android、组件、布局管理器的使用&#xff1b; 3&#xff0e;掌握Android中事件处理机制。 实验要求 第24章&#xff1a;初识Android任务一&#xff1a;完成Hello World…

死信队列和延迟队列_在实践中使用延迟队列

死信队列和延迟队列通常&#xff0c;在某些情况下&#xff0c;当您有某种工作或作业队列时&#xff0c;有必要不立即处理每个工作项或作业&#xff0c;而是要延迟一些时间。 例如&#xff0c;如果用户单击一个按钮来触发要完成的某项工作&#xff0c;而一秒钟后&#xff0c;用户…

python的代码在哪写_python代码在哪里编写

编写python代码&#xff0c;可以在自带的ide中写&#xff0c;也可以使用第三方编辑器&#xff0c;下面介绍几款常见的python IDE 1. VimVim 可以说是 Python 最好的 IDE。Vim 是高级文本编辑器&#xff0c;旨在提供实际的 Unix 编辑器‘Vi’功能&#xff0c;支持更多更完善的特…

nio框架中的多个Selector结构

随着并发数量的提高&#xff0c;传统nio框架采用一个Selector来支撑大量连接事件的管理和触发已经遇到瓶颈&#xff0c;因此现在各种nio框架的新版本都采用多个Selector并存的结构&#xff0c;由多个Selector均衡地去管理大量连接。这里以Mina和Grizzly的实现为例。 在Mina 2…

js 编码 php解码,浅谈php和js中json的编码和解码

php中1)编码$jsonStr json_encode($array)2)解码$arr json_decode($jsonStr)echo json_encode("中文", JSON_UNESCAPED_UNICODE);添加参数&#xff1a;JSON_UNESCAPED_UNICODE即可。测试环境&#xff1a;PHP Version 5.5.36js中1. 编码var str obj.toJSONString()…

如果您在2015年编写过Java代码-这是您不容错过的趋势

去年我们有机会遇到的最有趣趋势的实用概述 在这篇文章中&#xff0c;我们将回顾构成我们2015年对话的5个主题和新发展。与其他许多年终总结保持较高水平的不同&#xff0c;我们将做一个更实际的操作不用流行语 。 好吧&#xff0c;没有太多*流行语。 与往常一样&#xff0c;对…

mel滤波器组频率响应曲线_了解二阶滤波器的奈奎斯特图

在之前的文章中&#xff0c;我介绍了奈奎斯特图&#xff0c;然后我们通过检查奈奎斯特曲线和截止频率与一阶无源滤波器之间的关系&#xff0c;更详细地探索了这些类型的图。在本文中&#xff0c;我们将查看二阶滤波器的奈奎斯特图。二阶过滤器当我说“二阶”滤波器时&#xff0…

CJOJ 免费航班

Description 小Z在MOI比赛中获得了大奖&#xff0c;奖品是一张特殊的机 票。使用这张机票&#xff0c;可以在任意一个国家内的任意城市之间的免费飞行&#xff0c;只有跨国飞行时才会有额外的费用。小Z获得了一张地图&#xff0c;地图上有城市之间的飞机航班和 费用。已知从每个…