不删除侦听器–使用ListenerHandles

听一个可观察的实例并对它的变化做出反应很有趣。 做一些必要的事情来打断或结束这种聆听会变得很有趣。 让我们看看问题的根源和解决方法。

总览

这篇文章将首先讨论这种情况,然后再讨论常见的方法和问题所在。 然后,它将提供解决大多数问题的简单抽象。

尽管示例使用Java,但许多其他语言也存在缺陷。 提出的解决方案可以应用于所有面向对象的语言。 那些懒于自己在Java中实现抽象的人可以使用LibFX

情况

由Ky Olsen在CC-BY 2.0下发布。

由Ky Olsen在CC-BY 2.0下发布 。

假设我们想听听属性值的变化。 这很简单:

不支持删除的简单案例

private void startListeningToNameChanges(Property<String> name) {name.addListener((obs, oldValue, newValue) -> nameChanged(newValue));
}

现在假设我们要在特定间隔内中断监听或完全停止监听。

保持参考

解决此问题的最常见方法是保留对侦听器的引用,并保留对周围属性的引用。 根据具体的用例,实现会有所不同,但是它们都可以归结为以下形式:

默认方式删除侦听器

private Property<String> listenedName;
private ChangeListener<String> nameListener;...private void startListeningToNameChanges(Property<String> name) {listenedName = name;nameListener = (obs, oldValue, newValue) -> nameChanged(newValue);listenedName.addListener(nameListener);
}private void stopListeningToNameChanges() {listenedName.removeListener(nameListener);
}

尽管这看起来不错,但我确信这实际上是一个糟糕的解决方案(尽管是默认解决方案)。

首先,额外的引用使代码混乱。 很难让他们表达出为什么要留在身边的意图,因此它们降低了可读性。

其次,它们通过向类添加新的不变式来增加复杂性:该属性必须始终是添加了侦听器的属性。 否则,对removeListener的调用将无提示地执行任何操作,并且在将来的更改时仍将执行该侦听器。 放开这可能是讨厌的。 如果类很短,则坚持不变性是容易的,但如果变得越来越复杂,则可能成为问题。

第三,引用(尤其是该属性的引用)邀请与它们进行进一步的交互。 这可能不是故意的,但没有任何办法阻止下一个开发人员继续这样做(请参阅第一点)。 而且,如果有人确实开始对该物业进行操作,第二点将成为非常现实的风险。

这些方面已经使它无法成为默认解决方案。 但是还有更多! 在许多类中必须这样做会导致代码重复。 最后,上面的实现包含一个竞争条件。

侦听器句柄

大多数问题来自直接在需要中断/结束侦听的类中处理可观察对象和侦听器。 这是不必要的,所有这些问题都可以通过一个简单的抽象来解决: ListenerHandle

ListenerHandle

public interface ListenerHandle {void attach();void detach();
}

ListenerHandle保留对可观察对象和侦听器的引用。 在调用attach()detach()它要么将侦听器添加到可观察对象,要么将其删除。 为了将此语言嵌入语言,当前将侦听器添加到可观察对象的所有方法都应返回该组合的句柄。

现在剩下要做的就是针对所有可能的情况实际实现句柄。 或者说服那些开发您喜欢的编程语言的人来做。 这留给读者练习。

注意,这解决了上面提到的所有问题,除了争用条件之外。 有两种解决方法:

  • 处理实现可能本质上是线程安全的
  • 可以实现一个同步装饰器

LibFX中的ListenerHandles

作为Java开发人员,您可以使用LibFX ,它支持三个级别的侦听器句柄。

功能了解ListenerHandles

添加侦听ListenerHandle时, LibFX的所有可实现此功能而不与Java API冲突的功能都会返回一个ListenerHandle

以WebViewHyperlinkListener为例:

将“ ListenerHandle”获取到“ WebViewHyperlinkListener”

WebView webView;ListenerHandle eventProcessingListener = WebViews.addHyperlinkListener(webView, this::processEvent);

JavaFX实用程序

由于LibFX与JavaFX有紧密的联系(可能会想到!),它提供了一个实用程序类,该类将侦听器添加到可观察对象并返回句柄。 这适用于JavaFX中存在的所有可观察/侦听器组合。

例如,让我们看一下ObservableValue<T> / ChangeListener<? superT>的组合ChangeListener<? superT> ChangeListener<? superT>

'ListenerHandles'中的一些方法

public static <T> ListenerHandle createAttached(ObservableValue<T> observableValue,ChangeListener<? super T> changeListener);public static <T> ListenerHandle createDetached(ObservableValue<T> observableValue,ChangeListener<? super T> changeListener);

ListenerHandleBuilder

在所有其他情况下,即对于上面未涵盖的任何可观察/侦听器组合,可以使用构建器来创建手柄:

为自定义类创建“ ListenerHandle”

// These classes do not need to implement any special interfaces.
// Their only connection are the methods 'doTheAdding' and 'doTheRemoving',
// which the builder does not need to know about.
MyCustomObservable customObservable;
MyCustomListener customListener;ListenerHandles.createFor(customObservable, customListener).onAttach((obs, listener) -> obs.doTheAdding(listener)).onDetach((obs, listener) -> obs.doTheRemoving(listener)).buildAttached();

反应式编程

虽然这不是关于反应式编程的文章 ,但仍应提及。 查看ReactiveX (用于许多语言,包括Java,Scala,Python,C ++,C#和更多语言)或ReactFX (或此介绍性文章 )以了解一些实现。

反射

我们已经看到,从可观察对象中删除侦听器的默认方法会产生许多危害,需要避免。 侦听器句柄抽象提供了解决许多问题的干净方法,而LibFX提供了一种实现。

翻译自: https://www.javacodegeeks.com/2015/01/dont-remove-listeners-use-listenerhandles.html

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

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

相关文章

将url参数字符串转成数组

const url"/BaseDictionary?Type34"; const arrurl.split(?); // arr["/BaseDictionary","Type34"]; typeStr parse(arr[1]); // typeStr{Type: "34"}

uniapp /deep/设置uni-app组件样式时 h5生效 小程序失效问题解决

今天写uni-app的项目 设置uni-app扩展组件的样式 使用穿透/deep/ 发现小程序没有效果 h5有效果 //小程序无效 h5生效 /deep/ .uni-list-item .uni-list-item__container .uni-list-item__content .uni-list-item__content-title{color: #333333;font-size: 32upx;}加入一下代…

使用Google Guava Cache进行本地缓存

很多时候&#xff0c;我们将不得不从数据库或另一个Web服务获取数据或从文件系统加载数据。 在涉及网络呼叫的情况下&#xff0c;将存在固有的网络等待时间&#xff0c;网络带宽限制。 解决此问题的方法之一是在应用程序本地拥有一个缓存。 如果您的应用程序跨越多个节点&…

uva 1394poj 3517

递推&#xff0c;把问题转化为具有相同问题的子问题&#xff0c;通过子问题最后所剩余的编号&#xff0c;退出此问题所剩余的编号 #include <iostream> using namespace std; const int maxn1000010; int f[maxn]; int main() {int n,k,m;while(~scanf("%d %d %d&qu…

父级和子级div的点击事件相互影响

解决方法&#xff1a;event.stopPropagation();

Jersey和Spring Boot入门

除了许多新功能&#xff0c;Spring Boot 1.2还带来了Jersey支持。 这是吸引喜欢标准方法的开发人员的重要一步&#xff0c;因为他们现在可以使用JAX-RS规范构建RESTful API&#xff0c;并将其轻松部署到Tomcat或任何其他Springs Boot支持的容器中。 带有Spring平台的Jersey可以…

js对象数组(JSON) 根据某个共同字段分组

希望的是将下面的对象数组&#xff1a; [{"id":"1001","name":"值1","value":"111"},{"id":"1001","name":"值1","value":"11111"},{"id&quo…

用装饰器改变收藏

装饰图案 自从第一次学习编程设计模式以来&#xff0c;装饰器模式一直是我的最爱。 在我看来&#xff0c;这是一个很新颖的想法&#xff0c;比其他想法有趣得多。 不要误会我的意思&#xff0c;其他大多数人也引起了我的注意&#xff0c;但没有什么比装饰器模式更重要。 至今&a…

ASP.NET WebAPI 自定义ControllerSelector

呃..今天同事要实现客户端调用不同版本Controller的功能, 其实几句代码就搞定了.. 首先定义自己的ControllerSelector,代码如下: public class ShadowControllerSelector : IHttpControllerSelector{private readonly HttpConfiguration _configuration;public ShadowControlle…

MomentJS计算两个时间的差值diff方法

moment(endTime).diff(moment(startTime), years)moment(endTime).diff(moment(startTime), months)moment(endTime).diff(moment(startTime), days) // 开始时间和结束时间的时间差&#xff0c;以“天”为单位&#xff1b;endTime和startTime都是毫秒数moment(endTime).d…

JAX-RS 2.0:服务器端处理管道

这篇文章的灵感来自JAX-RS 2.0规范文档 &#xff08;附录C&#xff09;中的Processing Pipeline部分。 我喜欢它是因为它提供了JAX-RS中所有模块的漂亮快照-以准备好吞咽的胶囊形式&#xff01; 礼貌– JAX-RS 2.0规范文档 因此&#xff0c;我想到了使用此图简要概述不同的JA…

基于TCP/IP的文件服务器编程一例

来源&#xff0c;华清远见嵌入式学院实验手册&#xff0c;代码来源&#xff1a;华清远见曾宏安 实现的功能&#xff1a; 编写TCP文件服务器和客户端。客户端可以上传和下载文件 客户端支持功能如下&#xff1a; 1.支持一下命令 help 显示客户端所有命令和说明 list 显示服务器…

React 向children中传值,layouts

const newChild React.children.map(children,function(childItem){return React.cloneElement(childItem,{key:传递的数据}) })

Apache TomEE + JMS。 这从未如此简单。

我记得J2EE &#xff08;1.3和1.4&#xff09;的过去&#xff0c;使用JMS启动项目非常困难。 您需要安装JMS 代理 &#xff0c;创建主题或队列 &#xff0c;最后使用服务器配置文件和JNDI开始自己的战斗。 感谢JavaEE 6及其它&#xff0c;使用JMS确实非常简单。 但是使用Apach…

Struts2显示double价格格式0.00

在国际化资源文件中加入&#xff1a; format.money{0,number,0.00} jsp页面用struts标签&#xff1a; <s:text name"format.money">   <s:param name"value" value"priceName" /> </s:text> 输出格式&#xff1a;0.00转载于…

数组方法大全ES5+ES6

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录1. 使用 Array 构造函数2. 使用数组字面量表示法数组原型方法1. join()2.push()和pop()3.shift() 和 unshift()4.sort()5.reverse()6.concat()7.slice()8.splice()9.…

【Linux系统基础】(2)在Linux上部署MySQL、RabbitMQ、ElasticSearch、Zookeeper、Kafka、NoSQL等各类软件

实战章节&#xff1a;在Linux上部署各类软件 前言 为什么学习各类软件在Linux上的部署 在前面&#xff0c;我们学习了许多的Linux命令和高级技巧&#xff0c;这些知识点比较零散&#xff0c;同学们跟随着课程的内容进行练习虽然可以基础掌握这些命令和技巧的使用&#xff0c;…

使用Java 8流进行快速失败的验证

我已经失去了使用类似方法通过失败快速验证代码状态的次数&#xff1a; public class PersonValidator {public boolean validate(Person person) {boolean valid person ! null;if (valid) valid person.givenName ! null;if (valid) valid person.familyName ! null;if (…

找到数组最大值

const maxHight Math.max.apply(null, rowData && rowData.urlImage.map(ele > ele.long) || []);

JDK 7和JDK 8中大行读取速度较慢的原因

我之前发布了博客文章“使用JDK 7和JDK 8读取慢速行”&#xff0c;并且在该问题上有一些有用的评论来描述该问题。 这篇文章提供了更多解释&#xff0c;说明为何该文章中演示的文件读取&#xff08;并由Ant的LineContainsRegExp使用 &#xff09;在Java 7和Java 8中比在Java 6中…