Java的。 用于实现侦听器的正确模式

通常情况下,我遇到的情况是给定的对象需要有很多侦听器。 例如,我可能有

class Elephant { public void addListener( ElephantListener listener ) { ... } } 

但我会遇到很多这样的情况。 也就是说,我还将拥有一个具有TigerListenerTiger对象。 现在, TigerListenerElephantListener是完全不同的:

 interface TigerListener { void listenForGrowl( Growl qrowl ); void listenForMeow( Meow meow ); } 

 interface ElephantListener { void listenForStomp( String location, double intensity ); } 

我发现我总是必须在每个动物类中继续重新实现广播机制,并且实现总是相同的。 有首选模式吗?

您可以发送它,而不是每个Listener都有针对每种事件类型的特定方法,更改接口以接受通用Event类。 然后,如果需要,可以将Event子类化为特定的子类型,或者使其包含double intensity等状态。

然后TigerListener和ElephentListener成为

 interface TigerListener { void listen(Event event); } 

实际上,您可以进一步将此接口重构为普通的Listener

 interface Listener { void listen(Event event); } 

然后,您的Listener实现可以包含他们关心的特定事件所需的逻辑

 class TigerListener implements Listener { @Overrides void listen(Event event) { if (event instanceof GrowlEvent) { //handle growl... } else if (event instance of MeowEvent) { //handle meow } //we don't care about any other types of Events } } class ElephentListener { @Overrides void listen(Event event) { if (event instanceof StompEvent) { StompEvent stomp = (StompEvent) event; if ("north".equals(stomp.getLocation()) && stomp.getDistance() > 10) { ... } } } } 

订阅者和发布者之间的关键关系是发布者可以向订阅者发送事件,不一定是它可以向其发送某些类型的事件 – 这种类型的重构将该逻辑从接口推送到特定的实现中。

对于那些来这里只是想成为听众的人来说,这是一个更普遍的答案。 我总结了从CodePath 创建自定义侦听器 。 如果您需要更多解释,请阅读该文章。

这是步骤。

1.定义接口

这是在需要与某个未知父级进行通信的子类中。

 public class MyClass { // interface public interface MyClassListener { // add whatever methods you need here public void onSomeEvent(String title); } } 

2.创建一个监听器设置器

向子类添加私有侦听器成员变量和公共setter方法。

 public class MyClass { // add a private listener variable private MyClassListener mListener = null; // provide a way for another class to set the listener public void setMyClassListener(MyClassListener listener) { this.mListener = listener; } // interface from Step 1 public interface MyClassListener { public void onSomeEvent(String title); } } 

3.触发监听器事件

子对象现在可以在侦听器接口上调用方法。 一定要检查null,因为可能没有人在听。 (也就是说,父类可能没有为我们的侦听器调用setter方法。)

 public class MyClass { public void someMethod() { // ... // use the listener in your code to fire some event if (mListener != null) mListener.onSomeEvent("hello"); } // items from Steps 1 and 2 private MyClassListener mListener = null; public void setMyClassListener(MyClassListener listener) { this.mListener = listener; } public interface MyClassListener { public void onSomeEvent(String myString); } } 

4.在Parent中实现监听器回调

父级现在可以使用我们在子类中设置的侦听器。

例1

 public class MyParentClass { private void someMethod() { MyClass object = new MyClass(); object.setMyClassListener(new MyClass.MyClassListener() { @Override public void onSomeEvent(String myString) { // handle event } }); } } 

例2

 public class MyParentClass implements MyClass.MyClassListener { public MyParentClass() { MyClass object = new MyClass(); object.setMyClassListener(this); } @Override public void onSomeEvent(String myString) { // handle event } } 

我认为你做得对,因为你的界面具有语义价值并表达他们正在听的内容(例如咆哮和喵喵而不是踩踏)。 使用通用方法,您可以重用广播代码,但可能会失去可读性。

例如,有一个java.beans.PropertyChangeSupport ,它是一个实现Oberservers侦听值更改的实用程序。 它执行广播,但您仍需要在域类中实现该方法并委托给PropertyChangeSupport对象。 回调方法本身没有意义,广播的事件是基于字符串的:

 public interface PropertyChangeListener extends java.util.EventListener { void propertyChange(PropertyChangeEvent evt); } 

另一个是提供广播机制的java.util.Observable ,但它也不是最好的东西。

我喜欢ElephantListener.onStomp()

白板模式有不同的选择。 这使发布者和订阅者彼此断开连接,并且都不包含任何广播代码。 它们都只使用pub / sub的消息传递机制,并且没有与另一个直接连接。

这是OSGi平台中消息传递的常见模型。

尝试使用java kiss库,您将更快,更准确地完成此操作。

 import static kiss.API.*; class Elephant { void onReceiveStomp(Stomp stomp) { ... } } class Tiger { void onReceiveMeow(Meow meow) { ... } void onReceiveGrowl(Growl growl) { ... } } class TigerMeowGenerator extends Generator { // to add listeners, you get: // addListener(Object tiger); // anything with onReceiveMeow(Meow m); // addListener(meow->actions()); // any lambda // to send meow's to all listeners, use // send(meow) } 

生成器是线程安全且高效的(编写正确的生成器是最难的部分)。 它是Java Dev中的想法的实现。 期刊 – Java熟练听力(本地副本)