不删除侦听器–使用ListenerHandles
聽一個(gè)可觀察的實(shí)例并對(duì)它的變化做出反應(yīng)很有趣。 做一些必要的事情來打斷或結(jié)束這種聆聽會(huì)變得很有趣。 讓我們看看問題的根源和解決方法。
總覽
這篇文章將首先討論這種情況,然后再討論常見的方法和問題所在。 然后,它將提供解決大多數(shù)問題的簡(jiǎn)單抽象。
盡管示例使用Java,但許多其他語言也存在缺陷。 提出的解決方案可以應(yīng)用于所有面向?qū)ο蟮恼Z言。 那些懶于自己在Java中實(shí)現(xiàn)抽象的人可以使用LibFX 。
情況
由Ky Olsen在CC-BY 2.0下發(fā)布 。
假設(shè)我們想聽聽屬性值的變化。 這很簡(jiǎn)單:
不支持刪除的簡(jiǎn)單案例
private void startListeningToNameChanges(Property<String> name) {name.addListener((obs, oldValue, newValue) -> nameChanged(newValue)); }現(xiàn)在假設(shè)我們要在特定間隔內(nèi)中斷監(jiān)聽或完全停止監(jiān)聽。
保持參考
解決此問題的最常見方法是保留對(duì)偵聽器的引用,并保留對(duì)周圍屬性的引用。 根據(jù)具體的用例,實(shí)現(xiàn)會(huì)有所不同,但是它們都可以歸結(jié)為以下形式:
默認(rèn)方式刪除偵聽器
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); }盡管這看起來不錯(cuò),但我確信這實(shí)際上是一個(gè)糟糕的解決方案(盡管是默認(rèn)解決方案)。
首先,額外的引用使代碼混亂。 很難讓他們表達(dá)出為什么要留在身邊的意圖,因此它們降低了可讀性。
其次,它們通過向類添加新的不變式來增加復(fù)雜性:該屬性必須始終是添加了偵聽器的屬性。 否則,對(duì)removeListener的調(diào)用將無提示地執(zhí)行任何操作,并且在將來的更改時(shí)仍將執(zhí)行該偵聽器。 放開這可能是討厭的。 如果類很短,則堅(jiān)持不變性是容易的,但如果變得越來越復(fù)雜,則可能成為問題。
第三,引用(尤其是該屬性的引用)邀請(qǐng)與它們進(jìn)行進(jìn)一步的交互。 這可能不是故意的,但沒有任何辦法阻止下一個(gè)開發(fā)人員繼續(xù)這樣做(請(qǐng)參閱第一點(diǎn))。 而且,如果有人確實(shí)開始對(duì)該物業(yè)進(jìn)行操作,第二點(diǎn)將成為非?,F(xiàn)實(shí)的風(fēng)險(xiǎn)。
這些方面已經(jīng)使它無法成為默認(rèn)解決方案。 但是還有更多! 在許多類中必須這樣做會(huì)導(dǎo)致代碼重復(fù)。 最后,上面的實(shí)現(xiàn)包含一個(gè)競(jìng)爭(zhēng)條件。
偵聽器句柄
大多數(shù)問題來自直接在需要中斷/結(jié)束偵聽的類中處理可觀察對(duì)象和偵聽器。 這是不必要的,所有這些問題都可以通過一個(gè)簡(jiǎn)單的抽象來解決: ListenerHandle 。
ListenerHandle
public interface ListenerHandle {void attach();void detach(); }ListenerHandle保留對(duì)可觀察對(duì)象和偵聽器的引用。 在調(diào)用attach()或detach()它要么將偵聽器添加到可觀察對(duì)象,要么將其刪除。 為了將此語言嵌入語言,當(dāng)前將偵聽器添加到可觀察對(duì)象的所有方法都應(yīng)返回該組合的句柄。
現(xiàn)在剩下要做的就是針對(duì)所有可能的情況實(shí)際實(shí)現(xiàn)句柄。 或者說服那些開發(fā)您喜歡的編程語言的人來做。 這留給讀者練習(xí)。
注意,這解決了上面提到的所有問題,除了爭(zhēng)用條件之外。 有兩種解決方法:
- 處理實(shí)現(xiàn)可能本質(zhì)上是線程安全的
- 可以實(shí)現(xiàn)一個(gè)同步裝飾器
LibFX中的ListenerHandles
作為Java開發(fā)人員,您可以使用LibFX ,它支持三個(gè)級(jí)別的偵聽器句柄。
功能了解ListenerHandles
添加偵聽ListenerHandle時(shí), LibFX的所有可實(shí)現(xiàn)此功能而不與Java API沖突的功能都會(huì)返回一個(gè)ListenerHandle 。
以WebViewHyperlinkListener為例:
將“ ListenerHandle”獲取到“ WebViewHyperlinkListener”
WebView webView;ListenerHandle eventProcessingListener = WebViews.addHyperlinkListener(webView, this::processEvent);JavaFX實(shí)用程序
由于LibFX與JavaFX有緊密的聯(lián)系(可能會(huì)想到!),它提供了一個(gè)實(shí)用程序類,該類將偵聽器添加到可觀察對(duì)象并返回句柄。 這適用于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
在所有其他情況下,即對(duì)于上面未涵蓋的任何可觀察/偵聽器組合,可以使用構(gòu)建器來創(chuàng)建手柄:
為自定義類創(chuàng)建“ 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();反應(yīng)式編程
雖然這不是關(guān)于反應(yīng)式編程的文章 ,但仍應(yīng)提及。 查看ReactiveX (用于許多語言,包括Java,Scala,Python,C ++,C#和更多語言)或ReactFX (或此介紹性文章 )以了解一些實(shí)現(xiàn)。
反射
我們已經(jīng)看到,從可觀察對(duì)象中刪除偵聽器的默認(rèn)方法會(huì)產(chǎn)生許多危害,需要避免。 偵聽器句柄抽象提供了解決許多問題的干凈方法,而LibFX提供了一種實(shí)現(xiàn)。
翻譯自: https://www.javacodegeeks.com/2015/01/dont-remove-listeners-use-listenerhandles.html
總結(jié)
以上是生活随笔為你收集整理的不删除侦听器–使用ListenerHandles的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安卓网页加速器免费下载(安卓网页加速器)
- 下一篇: 使用Google Guava Cache