Java Listener继承

时间:2008-12-16 08:17:25

标签: java inheritance events listener

我有一个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方法类似的问题。
  4. 我正在使用Java 1.5版。

6 个答案:

答案 0 :(得分:12)

我没有看到BListener应该延伸AListener的原因。

您是否真的想强迫所有对B事件感兴趣的人也实施event1()

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

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

答案 1 :(得分:6)

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

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

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

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

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

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

接下来,匹配的EventSource接口:

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

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

public abstract class EventDispatcher<T> {
    private List<Listener<T>> listeners = new CopyOnWriteArrayList<T>();

    void addListener(Listener<T> listener) {
      listeners.add(listener);
    }    

    void removeListener(Listener<T> listener) {
      listeners.remove(listener);
    }

    void fireEvent(T event) {
      for (Listener<T> listener : listeners) {
        listener.event(event);
      } 
    }
}

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

public class Message {
}

public class InBox implements EventSource<Message> {

  private final EventDispatcher<Message> dispatcher = new EventDispatcher<Message>();

  public void addListener(Listener<Message> listener) {
    dispatcher.addListener(listener);
  }

  public void removeListener(Listener<Message> listener) {
    dispatcher.removeListener(listener);
  }

  public pollForMail() {
    // check for new messages here...
    // pretend we get a new message...

    dispatcher.fireEvent(newMessage);
  }
}

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

答案 2 :(得分:3)

我从你对saua的评论中明白,解雇B会自动解雇A.

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

class AEvent {}
class BEvent extends Event{}

interface EventListner<E extends AEvent>
{
   onEvent(E e);
}

class ListenerManager<E extends AEvent>{
    addListner(EventListener<? extends E>){}
    removeListner(EventListener<? extends E>){}
    fire(E e);
}

class A extends ListenerManager<AEvent>
{
}

class B extends ListenerManager<BEvent>
{
   A delegatorA;

  @Override addListener(EventListener<? extends BEvent> l)
  {
    super.addListner(l);
    delegatorA.addListener(l);
  }       

  @Override removeListener(EventListener<? extends BEvent> l)
  {
    super.removeListner(l);
    delegatorA.removeListener(l);
  }       

  @Override fire(BEvent b)
  {
    super.fire(b);
    a.fire(b)
  }

}

说明:在基类Listener Manager中共享用于管理侦听器的代码。 由于泛型编译时检查,B只能接收BListener。 点火B将自动触发A.

答案 3 :(得分:3)

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

我的描述

  • 您有一个基本类 A 执行一些 basicOperation

  • 您可能有一个更具体的子类 B ,此外还可以执行更多 specificOperations

如果是这种情况,则需要处理这两个事件(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<Listener> listeners = new ArrayList<Listener>();


    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"

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

答案 4 :(得分:1)

如果

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);
    }
  }
  ...
}

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

答案 5 :(得分:1)

根据我们对A&amp; B,我认为让BListener成为AListener的子接口令人困惑。顾名思义,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处理两种侦听器,正如你正确指出的那样。现在,考虑以下使用组合而不是继承的替代方法:

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