Java Listenerinheritance

我有一个java类来触发自定义java事件。 代码的结构如下:

public class AEvent extends EventObject { ... } public interface AListener extends EventListener { public void event1(AEvent event); } public class A { public synchronized void addAListener(AListener l) { .. } public synchronized void removeAListener(AListener l) { .. } protected void fireAListenerEvent1(AEvent event) { .. } } 

一切正常,但我想创建一个新的A子类(称之为B),它可能会触发一个新的事件。 我正在考虑以下修改:

 public class BEvent extends AEvent { ... } public interface BListener extends AListener { public void event2(BEvent event); } public class B extends A { public synchronized void addBListener(BListener l) { .. } public synchronized void removeBListener(BListener l) { .. } protected void fireBListenerEvent2(AEvent event) { .. } } 

这是正确的方法吗? 我在网上搜索示例,但找不到任何内容。

在这个解决方案中有一些我不喜欢的东西:

  1. BListener有两种方法,一种是使用AEvent ,另一种是使用BEvent作为参数。
  2. B类都有addAListeneraddBListener方法。 我应该使用私有关键字隐藏addAListener吗? [更新:无法隐藏私人关键字]
  3. fireAListenerEvent1fireBListenerEvent1方法类似的问题。

我正在使用Java 1.5版。

我没有看到BListener应该扩展AListener的原因。

你真的想强迫每个对B事件感兴趣的人也实现event1()吗?

此外,您无法添加addAListener() ,因为派生类不能降低父类中存在的方法的可见性。 此外,你不应该,或者你会违反Liskov替换原则 (每个B必须能够做A所能做的一切)。

作为最后一句话,我会使fire*()方法受到保护。 通常没有理由让它们公开,减少公共成员的数量可以保持公共界面的清洁。

不要使用inheritance,它不是你想要的,会导致脆弱和难以改变的设计。 组合是一种更灵活,更好的设计方法。 始终尝试尽可能精细地设计接口,因为它们不应该被更改为事件。 它们是您与系统其余部分的合同。 如果需要添加新function,则第一个选项是向事件添加更多信息。 如果这不合适,那么您应该设计一个新的界面来提供该事件。 这可以防止必须更改任何不受影响的现有代码。

这是我最喜欢的模式,我相信它通常被称为观察者。

创建一个新接口,定义该事件类型的方法(fooEvent()addFooEventListener()removeFooEventListener())。 在生成这些事件的具体类中实现此接口。 (我通常称之为SourcesFooEvent,FiresFooEvent,FooEventSource等)

如果要减少代码重复,可以构造一个辅助类来处理侦听器的注册,将它们存储在集合中,并提供一个用于发布事件的fire方法。

generics可以在这里提供帮助。 首先,一个通用的监听器接口:

 public interface Listener { void event(T event); } 

接下来,匹配的EventSource接口:

 public interface EventSource { void addListener(Listener listener); } 

最后是一个抽象基类,用于快速构造一个帮助器类来处理监听器的注册和事件调度:

 public abstract class EventDispatcher { private List> listeners = new CopyOnWriteArrayList(); void addListener(Listener listener) { listeners.add(listener); } void removeListener(Listener listener) { listeners.remove(listener); } void fireEvent(T event) { for (Listener listener : listeners) { listener.event(event); } } } 

您可以通过封装使用抽象的EventDispatcher,允许任何其他类轻松实现EventSource,同时不要求它扩展任何特定的类。

 public class Message { } public class InBox implements EventSource { private final EventDispatcher dispatcher = new EventDispatcher(); public void addListener(Listener listener) { dispatcher.addListener(listener); } public void removeListener(Listener listener) { dispatcher.removeListener(listener); } public pollForMail() { // check for new messages here... // pretend we get a new message... dispatcher.fireEvent(newMessage); } } 

希望这说明了类型安全(重要),灵活性和代码重用之间的良好平衡。

我从你对saua的评论中了解到,射击B会自动射击A.

为什么不使用单一类型的侦听器,然后混合一些inheritance,委托和generics?

 class AEvent {} class BEvent extends Event{} interface EventListner { onEvent(E e); } class ListenerManager{ addListner(EventListener){} removeListner(EventListener){} fire(E e); } class A extends ListenerManager { } class B extends ListenerManager { A delegatorA; @Override addListener(EventListener l) { super.addListner(l); delegatorA.addListener(l); } @Override removeListener(EventListener l) { super.removeListner(l); delegatorA.removeListener(l); } @Override fire(BEvent b) { super.fire(b); a.fire(b) } } 

说明:在基类Listener Manager中共享用于管理侦听器的代码。 B只能接收BListener,因为generics编译时检查。 射击B将自动射击A.

在我看来,你可以保持简单。

我的讲述

  • 你有一个执行一些basicOperation的基本类A.

  • 您可能有一个更具体的子类B ,此外可以执行一些更具体的操作

如果是这种情况,则需要处理这两个事件( 基本为A和基本 + 特定于B)

好吧,您不需要重载方法来执行此操作,您唯一需要做的就是为特定事件添加特定处理程序(或侦听器)。

可能是事件是“基本”的情况,这很好。

但是当事件具体时,您需要做出相应的反应。 那么,我要做的是特定的侦听器中添加一个检查来区分特定事件,如下所示:

  if( whichEvent instanceof SpecificEvent ) { SpecificEvent s = ( SpecificEvent ) whichEvent; // Do something specific here... } 

就是这样。

您对问题的描述过于抽象,因此无法提出具体的解决方案。 然而,如果很难解释你想要达到的目标,那么你可能需要首先重新分析问题所在。

如果我上面的理解是正确的(你需要处理基本的 + 特定的一些),下面的下面冗长的代码可能会有所帮助。

最好的祝福


 import java.util.*; class A { // All the listener will be kept here. No matter if basic or specific. private List listeners = new ArrayList(); public void add( Listener listener ) { listeners.add( listener ); } public void remove( Listener listener ) { listeners.remove( listener ); } // In normal work, this class just perform a basic operation. public void normalWork(){ performBasicOperation(); } // Firing is just firing. The creation work and the // operation should go elsewhere. public void fireEvent( Event e ) { for( Listener l : listeners ) { l.eventHappened( e ); } } // A basic operation creates a basic event public void performBasicOperation() { Event e = new BasicEvent(); fireEvent( e ); } } // Specialized version of A. // It may perform some basic operation, but also under some special circumstances // it may perform an specific operation too class B extends A { // This is a new functionality added by this class. // Hence an specifi event is fired. public void performSpecificOperation() { Event e = new SpecificEvent(); // No need to fire in different way // an event is an event and that's it. fireEvent( e ); } // If planets are aligned, I will perform // an specific operation. public void normalWork(){ if( planetsAreAligned() ) { performSpecificOperation(); } else { performBasicOperation(); } } private boolean planetsAreAligned() { //return new Random().nextInt() % 3 == 0; return true; } } // What's an event? Something from where you can get event info? interface Event{ public Object getEventInfo(); } // This is the basic event. class BasicEvent implements Event{ public Object getEventInfo() { // Too basic I guess. return "\"Doh\""; } } // This is an specific event. In this case, an SpecificEvent IS-A BasicEvent. // So , the event info is the same as its parent. "Doh". // But, since this is an SpecificEvent, it also has some "Specific" features. class SpecificEvent extends BasicEvent { // This method is something more specific. // There is no need to overload or create // different interfaces. Just add the new specific stuff public Object otherMethod() { return "\"All I can say is , this was an specific event\""; } } // Hey something just happened. interface Listener { public void eventHappened( Event whichEvent ); } // The basic listner gets information // from the basic event. class BasicEventListener implements Listener { public void eventHappened( Event e ) { System.out.println(this.getClass().getSimpleName() + ": getting basic functionality: " + e.getEventInfo()); } } // But the specific listner may handle both. // basic and specific events. class SpecificListener extends BasicEventListener { public void eventHappened( Event whichEvent ) { // Let the base to his work super.eventHappened( whichEvent ); // ONLY if the event if of interest to THIS object // it will perform something extra ( that's why it is specific ) if( whichEvent instanceof SpecificEvent ) { SpecificEvent s = ( SpecificEvent ) whichEvent; System.out.println(this.getClass().getSimpleName() + ": aaand getting specific functionality too: " + s.otherMethod() ); // do something specific with s } } } // See it run. // Swap from new A() to new B() and see what happens. class Client { public static void main( String [] args ) { A a = new B(); //A a = new A(); a.add( new BasicEventListener() ); a.add( new SpecificListener() ); a.normalWork(); } } 

样本输出:

 BasicEventListener: getting basic functionality: "Doh" SpecificListener: getting basic functionality: "Doh" SpecificListener: aaand getting specific functionality too: "All I can say is , this was an specific event" 

更进一步,你甚至可以摆脱接口,以保持简单

如果

 public class BEvent extends AEvent { ... } public interface BListener extends AListener { public void event2(BEvent event); } 

你不能做的事情如下:

 public class B extends A { @Override public synchronized void addAListener(AListener l) { if (l instanceof BListener) { ... } else { super.addAListener(l); } } ... } 

正如我在评论中所说,我不确定你到底想要实现什么目标? 从哪里召唤谁,在被召唤时需要做什么?

根据我们对AB之间关系的一些信息,我认为让BListener成为BListener的子接口令人困惑。 顾名思义, BListener应该监听BEvent ,它已经AEvent的子类。 为清楚起见,听众应具有辨别力; 它们不应该不必要地重叠。 此外,不需要这样的重叠侦听器,因为您已经在B类中定义了单独的方法来处理不同类型的侦听器。

为了说明我的观点,请考虑以下代码示例:

 public class MovableMouseEvent extends EventObject public class ClickableMouseEvent extends MovableMouseEvent public interface MovableMouseListener extends EventListener // mouseMoved(MovableMouseEvent) public interface ClickableMouseListener extends MovableMouseListener // mouseClicked(ClickableMouseEvent) public class MovableMouseWidget // {addMovableMouseListener,removeMovableMouseListener}(MovableMouseListener) // fireMovableMouseEvent(MovableMouseEvent) public class ClickableMouseWidget extends MovableMouseWidget // {addClickableMouseListener,removeClickableMouseListener}(ClickableMouseListener) // fireClickableMouseEvent(ClickableMouseEvent) 

这个设计有效,但是令人困惑,因为ClickableMouseListener处理两种事件,而ClickableMouseWidget处理两种侦听器,正如你正确指出的那样。 现在,考虑以下使用组合而不是inheritance的替代方法:

 public class MouseMoveEvent extends EventObject // note the name change public class MouseClickEvent extends EventObject // don't extend MouseMoveEvent public interface MouseMoveListener extends EventListener // mouseMoved(MouseMoveEvent) public interface MouseClickListener extends EventListener // don't extend MouseMoveListener // mouseClicked(MouseClickEvent) public interface MouseMoveObserver // {addMouseMoveListener,removeMouseMoveListener}(MouseMoveListener) // fireMouseMoveEvent(MouseMoveEvent) public interface MouseClickObserver // {addMouseClickListener,removeMouseClickListener}(MouseClickListener) // fireMouseClickEvent(MouseClickEvent) public class MovableMouseWidget implements MouseMoveObserver public class ClickableMouseWidget implements MouseMoveObserver, MouseClickObserver