通常我会遇到一个给定对象需要有很多侦听器的情况。例如,我可能有
class Elephant {
public void addListener( ElephantListener listener ) { ... }
}
但我会遇到很多这样的情况。也就是说,我还会有一个Tiger
对象,它会有TigerListener
个。现在,TigerListener
和ElephantListener
完全不同:
interface TigerListener {
void listenForGrowl( Growl qrowl );
void listenForMeow( Meow meow );
}
,而
interface ElephantListener {
void listenForStomp( String location, double intensity );
}
我发现我必须不断在每个动物类中重新实现广播机制,并且实现总是相同的。是否有首选模式?
答案 0 :(得分:26)
不是每个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) {
...
}
}
}
}
订阅者和发布者之间的关键关系是发布者可以向订阅者发送事件,不一定是它可以向其发送某些类型的事件 - 这种类型的重构将该逻辑从接口推送到具体实施。
答案 1 :(得分:10)
对于那些来这里只想做一个听众的人来说,这是一个更普遍的答案。我在CodePath中总结了Creating Custom Listeners。如果您需要更多解释,请阅读该文章。
以下是步骤。
这是在需要与某个未知父级进行通信的子类中。
public class MyClass {
// interface
public interface MyClassListener {
// add whatever methods you need here
public void onSomeEvent(String title);
}
}
将私有侦听器成员变量和公共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);
}
}
子对象现在可以在侦听器接口上调用方法。一定要检查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);
}
}
父级现在可以使用我们在子类中设置的侦听器。
public class MyParentClass {
private void someMethod() {
MyClass object = new MyClass();
object.setMyClassListener(new MyClass.MyClassListener() {
@Override
public void onSomeEvent(String myString) {
// handle event
}
});
}
}
public class MyParentClass implements MyClass.MyClassListener {
public MyParentClass() {
MyClass object = new MyClass();
object.setMyClassListener(this);
}
@Override
public void onSomeEvent(String myString) {
// handle event
}
}
答案 2 :(得分:3)
我认为你这样做是正确的,因为你的界面具有语义价值并表达他们正在听的内容(例如咆哮和喵喵而不是踩踏)。使用通用方法,您可以重用广播代码,但可能会失去可读性。
例如,java.beans.PropertyChangeSupport
是一个用于实现Oberservers监听值变化的实用程序。它执行广播,但您仍需要在域类中实现该方法并委托给PropertyChangeSupport对象。回调方法本身没有意义,广播的事件是基于字符串的:
public interface PropertyChangeListener extends java.util.EventListener {
void propertyChange(PropertyChangeEvent evt);
}
另一个是java.util.Observable
提供广播机制,但它也不是最好的东西。
我喜欢ElephantListener.onStomp()
答案 3 :(得分:1)
Whiteboard Pattern有不同的选项。这使发布者和订阅者彼此断开连接,并且都不包含任何广播代码。它们都只使用pub / sub的消息传递机制,并且没有与另一个直接连接。
这是OSGi平台中消息传递的常用模型。
答案 4 :(得分:1)
我专门为此创建了一个 Signals 库。删除“重新实现广播机制”中涉及的锅炉代码。
信号是从接口自动创建的对象。它具有添加侦听器和调度/广播事件的方法。
看起来像这样:
interface Chat{
void onNewMessage(String s);
}
class Foo{
Signal<Chat> chatSignal = Signals.signal(Chat.class);
void bar(){
chatSignal.addListener( s-> Log.d("chat", s) ); // logs all the messaged to Logcat
}
}
class Foo2{
Signal<Chat> chatSignal = Signals.signal(Chat.class);
void bar2(){
chatSignal.dispatcher.onNewMessage("Hello from Foo2"); // dispatches "Hello from Foo2" message to all the listeners
}
}
在本例中,Foo2
是通过 Chat
接口的新消息的广播者。 Foo
然后收听这些并将其记录到 logcat。
SignalsHelper
)答案 5 :(得分:-1)
尝试使用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<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)
}
生成器是线程安全且高效的(编写正确的生成器是最难的部分)。这是一个实现的想法 Java Dev. Journal - Skilled Listening in Java (local copy)