Java 爪哇。实现监听器的正确模式
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2975935/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java. Correct pattern for implementing listeners
提问by Jake
Very typically I have a situation where a given object will need to have many listeners. For instance, I might have
非常典型的情况是,给定的对象需要有许多侦听器。例如,我可能有
class Elephant {
public void addListener( ElephantListener listener ) { ... }
}
but I'll have many such situations. That is, I'll also have a Tiger
object that'll have TigerListener
s. Now, TigerListener
s and ElephantListener
s are quite different:
但我会有很多这样的情况。也就是说,我也会有一个Tiger
带有TigerListener
s的对象。现在,TigerListener
s 和ElephantListener
s 完全不同:
interface TigerListener {
void listenForGrowl( Growl qrowl );
void listenForMeow( Meow meow );
}
while
尽管
interface ElephantListener {
void listenForStomp( String location, double intensity );
}
I find that I always have to keep re-implementing the broadcasting mechanism in each animal class, and the implementation is always the same. Is there a preferred pattern?
我发现我总是要不断地在每个动物类中重新实现广播机制,并且实现总是相同的。有没有首选模式?
回答by matt b
Instead of each Listener
having specific methods for every event type you can send it, change the interface to accept a generic Event
class. You can then subclass Event
to specific subtypes if you need, or have it contain state such as double intensity
.
Listener
您可以将接口更改为接受通用Event
类,而不是每个事件类型都具有特定的方法。然后,您可以Event
根据需要将子类化为特定的子类型,或者让它包含诸如double intensity
.
TigerListener and ElephentListener then become
TigerListener 和 ElephentListener 然后变成
interface TigerListener {
void listen(Event event);
}
In fact, you can then further refactor this interface into a plain Listener
:
事实上,你可以进一步将此接口重构为一个普通的Listener
:
interface Listener {
void listen(Event event);
}
Your Listener
implementations can then contain the logic that they need for the specific events they care about
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) {
...
}
}
}
}
The key relationship between the subscriber and the publisher is that the publisher can send events to the subscribers, it isn't necessarily that it can send it certain types of events - this type of refactoring pushes that logic from the interface down into the specific implementations.
订阅者和发布者之间的关键关系是发布者可以向订阅者发送事件,它不一定可以向订阅者发送某些类型的事件 - 这种类型的重构将逻辑从接口推送到具体实现.
回答by mhaller
I think you're doing it correct, since your interfaces have semantic value and express what they are listening to (e.g. growls and meows instead of stomps). With a generic approach, you may be able to reuse the broadcasting code, but you may lose the readability.
我认为您做对了,因为您的界面具有语义价值并表达了他们正在聆听的内容(例如咆哮和喵喵声而不是跺脚)。使用通用方法,您可能能够重用广播代码,但可能会失去可读性。
For example, there is the java.beans.PropertyChangeSupport
which is a utility for implementing Oberservers listening for value changes. It does the broadcasting, but you still need to implement the method in your domain class and delegate to the PropertyChangeSupport object. The callback methods are meaningless by themselves, and the events broadcasted are String-based:
例如,java.beans.PropertyChangeSupport
这是一个实用程序,用于实现监听值变化的观察者服务器。它进行广播,但您仍然需要在域类中实现该方法并委托给 PropertyChangeSupport 对象。回调方法本身没有意义,广播的事件是基于字符串的:
public interface PropertyChangeListener extends java.util.EventListener {
void propertyChange(PropertyChangeEvent evt);
}
Another one is java.util.Observable
which provides the broadcasting mechanism, but it's also not the best thing imho.
另一个是java.util.Observable
提供广播机制,但恕我直言,这也不是最好的事情。
I like ElephantListener.onStomp()
我喜欢 ElephantListener.onStomp()
回答by Robin
A different options is the Whiteboard Pattern. This disconnects the publisher and subscriber from each other, and neither will contain any broadcasting code. They both simply use a messaging mechanism for pub/sub and neither has any direct connection to the other.
一个不同的选项是Whiteboard Pattern。这会断开发布者和订阅者之间的连接,并且两者都不会包含任何广播代码。它们都简单地使用一种用于发布/订阅的消息传递机制,并且彼此之间都没有任何直接连接。
This is a common model for messaging in an OSGi platform.
这是 OSGi 平台中消息传递的常用模型。
回答by Warren MacEvoy
Try the java kisslibrary and you will get this done faster and more correctly.
试试javakiss库,你会更快、更正确地完成这项工作。
import static kiss.API.*;
class Elephant {
void onReceiveStomp(Stomp stomp) { ... }
}
class Tiger {
void onReceiveMeow(Meow meow) { ... }
void onReceiveGrowl(Growl growl) { ... }
}
class TigerMeowGenerator extends Generator<Meow> {
// 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)
}
The generator is thread-safe and efficient (writing correct generators is the hardest part). It is an implementation of the ideas in Java Dev. Journal - Skilled Listening in Java (local copy)
生成器是线程安全且高效的(编写正确的生成器是最难的部分)。它是Java Dev 中思想的实现 。Journal - 熟练的 Java 听力(本地副本)
回答by Suragch
This is a more general answer for people who come here just wanting to make a listener. I am summarizing Creating Custom Listenersfrom CodePath. Read that article if you need more explanation.
对于来到这里只是想成为听众的人来说,这是一个更一般的答案。我正在总结从 CodePath创建自定义侦听器。如果您需要更多解释,请阅读该文章。
Here are the steps.
以下是步骤。
1. Define an Interface
1.定义一个接口
This is in the child class that needs to communicate with some unknown parent.
这是在需要与某个未知父级通信的子类中。
public class MyClass {
// interface
public interface MyClassListener {
// add whatever methods you need here
public void onSomeEvent(String title);
}
}
2. Create a Listener Setter
2. 创建一个监听器设置器
Add a private listener member variable and a public setter method to the child class.
向子类添加私有侦听器成员变量和公共 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. Trigger Listener Events
3. 触发监听事件
The child object can now call methods on the listener interface. Be sure to check for null because there might not be anyone listening. (That is, the parent class might not have called the setter method for our listener.)
子对象现在可以调用侦听器接口上的方法。一定要检查 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. Implement the Listener Callbacks in the Parent
4.在Parent中实现监听器回调
The parent can now use the listener that we set up in the child class.
父类现在可以使用我们在子类中设置的侦听器。
Example 1
示例 1
public class MyParentClass {
private void someMethod() {
MyClass object = new MyClass();
object.setMyClassListener(new MyClass.MyClassListener() {
@Override
public void onSomeEvent(String myString) {
// handle event
}
});
}
}
Example 2
示例 2
public class MyParentClass implements MyClass.MyClassListener {
public MyParentClass() {
MyClass object = new MyClass();
object.setMyClassListener(this);
}
@Override
public void onSomeEvent(String myString) {
// handle event
}
}