流畅和稳定的API的Lambda

几周前,我写了关于Java 8 lambda的介绍 。 在本简介中,我解释了什么是lambda以及如何将它们与Java 8中也引入的新Stream API结合使用。

Stream API为集合提供了更实用的接口。 此接口在很大程度上取决于lambda。 但是,lambda不仅具有改进的收集处理能力,还具有更多优势

Lambda为您提供了构建更流畅的API的机会。 为了说明这一点,作为示例,我喜欢使用UserStore ,它有助于使用数据库获取和保存用户。 它的公共API通常如下所示。

public interface UserStore {User find(Long id);List<User> findByLastname(String lastname);List<User> findByCompany(String company);..
}

findBy方法的列表通常比我在此处包括的两个方法更长。 随着系统的发展,可能还会有其他人。 尽管可行,但实际上所有这些方法都可以完成相同的事情。 他们返回具有匹配特定值的属性的所有用户。

一些框架提供了解决此问题的方法。 如果您使用过Hibernate,您可能会知道它们通过findByExample提供了解决方法,其中您提供了User作为示例对象,提供了查询的属性和值。 使用此示例对象中设置的任何值进行查询,而从查询中排除任何为null字段。 您可以稍微调整一下这种行为,但是这种方法存在许多问题。 考虑默认值,必填字段(即无法填写的字段)
null )和不变性。 iBatis,MyBatis以及Spring Data使用代码生成来节省您实现所有这些方法的时间,而使API充满了findBy方法列表。

这些变通办法可能会走很长一段路,但是它们确实留下了自己的特定问题。 另一种方法是使用lambda。

Lambda可以帮助我们将查询部分与过滤器规范分离。 让我们将findBy函数更改为接受lambda的单个函数。

public interface UserStore {User find(Long id);List<User> findBy(Predicate<User> p);
}

那是一个更好的API。 显然,谓词检查User对象有点天真。 您通常希望使用数据库查询进行过滤。 尽管如此,它仍然很好地满足了本示例的目的,您可以尝试使用自己的lambda来使用数据库查询进行过滤。 [注意: Predicate是Java 8附带的,位于java.util.function包中。

至少在以前的API中,这些谓词被捆绑在一个地方之前,您可能会生气,我们仍然可以捆绑(通用)谓词。 例如,通过创建一个包含它们的实用程序类UserPredicates

public final class UserPredicates {public static Predicate<User> lastname(String matcher) {return candidate -> matcher.equals(candidate.getLastname());}
}

使用新的UserStore API变得非常简单。

static import UserPredicates.lastname;userStore.findBy(lastname("<lastname>");

不过, UserStore中还有一件事确实让我感到困扰。 find(id)函数返回一个用户。 但是,如果没有这样的用户呢?

可选的

为了对此进行改进,我们可以(并且应该)查看Java 8的另一个新功能,Optional。 这是monad的Java实现。 它看起来很像Scala的Option

使用Optional我们可以更好地表示一个函数可以返回一个值,但不一定返回一个值,并防止使用null 。 在我们的find(id)示例中,返回Optional明确表示我们可能找到具有所请求ID的用户,但可能不存在这样的用户。

public interface UserStore {Optional<User> find(Long id);List<User> findBy(Predicate p);
}

该API现在不仅记录了您可能会获得用户的事实,而且从未返回null 。 我认为永不返回null的API更加安全。 有一天,一个新的程序员可能没有意识到find可以返回null并且结果是一个null指针异常。 只是希望团队能够在生产之前就抓住它。 只要不使用null ,就很容易防止空指针异常。

我们可以使用Optional上的函数从用户那里获取一个值(如果有的话),或者从一个默认值中获取一个值。 例如,为了安全地获取用户的姓氏,我们编写以下内容。

Optional<User> user = userStore.find(id);
String lastname = user.map(User::getLastname).orElse("");

这段代码表达力很强,不需要太多解释。 如果有用户,请获取其姓氏。 否则,获取一个空字符串。

如果我们需要向用户发送密码重置电子邮件(如果找到)怎么办?

Optional<User> user = userStore.find(id);
user.ifPresent(passwordReset::send);

如果找到用户,则发送密码重置,否则什么也没有发生。

由于Java不像其他可能提供的其他语言(例如Haskell,Clojure和Scala)那样支持解构,因此我们仅限于Optional的功能。 这使得Optional比任何一种其他语言的等效功能都弱。

建造者

当然,不仅存储库的API都可以从lambda中受益。 Optional也是受益于lambda的API的一个很好的示例。 就我个人而言,我还发现lambda特别有用,可以代替过去的过往建造者。 通常不通过将特定的构建器传递给函数,而是通过从函数中生成一个构建器来改善去耦。 让我向您展示一个示例,用于发送电子邮件以阐明该想法。

public interface Mailer {void sendTextMessage(TextMessageBuilder message);void sendMimeMessage(MimeMessageBuilder message);
}

要使用Mailer我们需要将特定的构建器传递给它。 这些构建器具有通用的界面,但是它们构建的消息类型不同。 Mailer具有不同的方法,因为它必须根据所使用的类型添加不同的信息。 因此,任何客户端代码都紧密耦合以传递正确的构建器。

您可能会怀疑,这是lambda有用的地方。 Mailer函数可以创建所需的生成器并将其产生给lambda,而不是要求客户端创建生成器并将其传递给客户端。

public interface Mailer {void sendTextMessage(MessageConfigurator configurator);void sendMimeMessage(MessageConfigurator configurator);@FunctionalInterfaceinterface MessageConfigurator {MessageBuilder configure(MessageBuilder message);}
}

要使用Mailer我们要做的就是提供一个lambda来构建消息。

mailer.sendTextMessage(message ->message.from(sender).to(recipients).subject("APIs").body("Lambdas can make for more fluent and stable APIs")
);

API现在更加稳定。 客户端代码与特定构建器中的任何更改都脱钩,并且只要构建器上的功能保持兼容就不会中断。

正如示例帮助我展示的那样,lambda可以帮助您构建更流畅和稳定的API,这些API更具意图。 这些API不需要太多的文档供其他程序员使用,因为实际上很难使它们弄错。 作为一般准则,我更喜欢清晰明了的代码而不是文档。 修复,不记录。

当然,我在本文中仅显示了一些示例。 Lambda不仅适用于此处的示例,而且适用范围更广。 我希望本文能为您提供一些有关lambda可以帮助您的新见解,并希望您能想到它们如何改善您的代码。

参考: Software Craft博客上的JCG合作伙伴 Bart Bakker 提供的Fluent和Stable API的Lambda 。

翻译自: https://www.javacodegeeks.com/2013/11/lambdas-for-fluent-and-stable-apis.html

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

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

相关文章

linux 内存使用原理,linux中内存使用原理

首先介绍一下linux中内存是如何使用的。当有应用需要读写磁盘数据时&#xff0c;由系统把相关数据从磁盘读取到内存&#xff0c;如果物理内存不够&#xff0c;则把内存中的部分数据导入到磁盘&#xff0c;从而把磁盘的部分空间当作虚拟内存来使用&#xff0c;也称为Swap。如果给…

Confluence 6 站点备份和恢复

Atlassian 推荐针对生产环境中安装使用的 Confluence 使用原始数据库工具备份策略。 在默认的情况下&#xff0c;Confluence 每天都会备份所有数据和附件到 XML 文件备份中。这些文件被称为 XML 站点备份&#xff0c;同时这些文件存储在 Confluence home 目录中的 backups 目录…

休眠事实:等于和HashCode

每个Java对象都继承了equals和hashCode方法&#xff0c;但它们仅对Value对象有用&#xff0c;对面向无状态行为的对象毫无用处。 尽管使用“ ”运算符比较引用很简单&#xff0c;但是对于对象相等而言&#xff0c;事情要复杂一些。 由于您负责告诉平等性对特定对象类型的含义…

从mysql向HBase+Phoenix迁移数据的心得总结

* 转载请注明出处 - yosql473 - 格物致知&#xff0c;经世致用 mysql -> HBase Phoenix 1.总体方案有哪些&#xff1f; 1&#xff09;通过Sqoop直接从服务器(JDBC方式)抽取数据到HBase中 因为数据量非常大&#xff0c;因此优先考虑用Sqoop和MR抽取。 使用Sqoop抽取数据有一…

玩转异步 JS :async/await 简明教程(附视频下载)

课程介绍 在软件开发领域&#xff0c;简洁的代码 > 容易阅读的代码 > 容易维护的代码&#xff0c;而 ES2017 中的 async/await 特性能让我们编写出相比回调地狱和 Promise 链式调用更直观、更容易理解的代码&#xff0c;await 关键字接收一个 Promise&#xff0c;等待代码…

linux 无法找到函数定义,找到定义Linux函数的位置

使用手册页对于基本的C函数&#xff0c;该手册页应该工作。man 2 readman 3 printf第2节为系统调用(直接到内核)&#xff0c;而第3是用于标准C库调用。您通常可以省略该部分&#xff0c;并且人将自己弄清楚您需要什么。请注意&#xff0c;您可能需要采取额外步骤在系统上获取与…

序列自动机—— [FJOI2016]所有公共子序列问题

序列自动机&#xff1a; 是一个处理子序列的自动机。就这样。 建造&#xff1a;&#xff08;By猫老师&#xff1a;immoralCO猫&#xff09; s[] next[][26] memset(next[n], -1, 26<<2); for(int i n; i; --i) {memcpy(next[i - 1], next[i], 26 << 2);next[i - 1…

1000种对Java的响应没有死

当一篇评论发表1000条评论时&#xff0c;值得考虑一下。 我上周的社论“ 如果Java即将死&#xff0c;它肯定看起来非常健康 ”在各个开发人员社区中都感到不安 。 在Reddit&#xff0c;Hacker News和Slashdot之间&#xff0c;它收到了1000多个评论。 奇怪的是&#xff0c;很少…

java导包

下载响应的zip文件&#xff0c;就可以导入了&#xff0c;导入src目录也是可以的。 转载于:https://www.cnblogs.com/liaoxiaolao/p/9902062.html

分享轮子-flutter下拉刷新上拉加载

flutter下拉上拉组件轮子 什么是flutter? 首先说下flutter,估计这个应该挺多人没听过flutter这个框架,它是一个google推出的跨平台的移动应用UI框架,和React Native是同样的目的,支持三大平台:Android,Ios,还有一个是google新出的系统,忘了叫什么...本人React Native也是用过…

linux块设备驱动中断程序,linux设备驱动归纳总结(六):1.中断的实现

linux设备驱动归纳总结(六)&#xff1a;1.中断的实现xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx一、什么是中断中断分两种&#xff1a;1)中断&#xff0c;又叫外部中断或异步中断&#xff0c;它的产生是由于外设向处理器发出中断…

(8)Python判断结构

转载于:https://www.cnblogs.com/hankleo/p/9170325.html

Java:本地最小语言

在1996年至2002年之间&#xff0c;我用Java编写了成千上万行代码。我用Java 1.0到Java 1.4编写了Web框架&#xff0c;电子表格以及更多内容。 与90年代中期&#xff08;预模板&#xff09;的C 相比&#xff0c;Java是一种完全令人惊奇的语言。 JVM是所有计算机语言的最佳运行时…

History of program(1950-2020)

1957年 约翰巴科斯&#xff08;John Backus&#xff09;创建了是全世界第一套高阶语言&#xff1a;FORTRAN。 John Backus1959年 葛丽丝霍普&#xff08;Grace Hopper&#xff09;创造了现代第一个编译器A-0 系统&#xff0c;以及商用电脑编程语言“COBOL”&#xff0c;被誉为C…

关于 Nuxt 集成ueditor的一些坑(包括图片上传)前端部分

最近公司接了一个项目&#xff0c;里面用到富文本编辑器&#xff0c;刚开始用的是vue-quill-editor&#xff0c;这个编辑器轻量、好用。最重要的是它有专门正对nuxt的版本&#xff0c;很容易配置&#xff0c;可以放心使用&#xff0c;不用担心bug之类的&#xff0c;遇到问题&am…

linux 线程带参数,Linux中多线程编程并传递多个参数的简单例子

今天上午实验了Linux下的多线程编程&#xff0c;并将多个参数传递给线程要执行的函数。以下是实验程序的源代码&#xff1a;/*********************** pthread.c ***************************/#include #include #include #include #include struct argument{int num;char stri…

*Codeforces989D. A Shade of Moonlight

数轴上$n \leq 100000$个不重叠的云&#xff0c;给坐标&#xff0c;长度都是$l$&#xff0c;有些云速度1&#xff0c;有些云速度-1&#xff0c;风速记为$w$&#xff0c;问在风速不大于$w_{max}$时&#xff0c;有几对云可能在0相遇。每一对云单独考虑。 多动一不动--相对运动。假…

undefined reference 问题各种情况分析

扒自网友文章 关于undefined reference这样的问题&#xff0c;大家其实经常会遇到&#xff0c;在此&#xff0c;我以详细地示例给出常见错误的各种原因以及解决方法&#xff0c;希望对初学者有所帮助。 1. 链接时缺失了相关目标文件&#xff08;.o&#xff09; 测试代码如下&a…

Spring交易可见性

在初始化应用程序上下文时&#xff0c;Spring遇到带有Transactional标记的类时会创建代理。 Transactional可以应用于类级别或方法级别。 在类级别应用它意味着该类中定义的所有公共方法都是事务性的。 Spring创建的代理类型&#xff0c;即Jdk代理或CGLIB代理&#xff0c;取决于…

Axios 作弊表(Cheat Sheet)

英文原文链接 GET 请求 // Make a request for a user with a given ID axios.get(/user?ID12345).then(function (response) {console.log(response);}).catch(function (error) {console.log(error);});// Optionally the request above could also be done as axios.get(…