7

我有一个触发自定义 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有两种方法,一种使用AEventBEvent一种作为参数。
  2. B类都有addAListeneraddBListener方法。我应该用 private 关键字隐藏 addAListener 吗?[更新:无法使用 private 关键字隐藏]
  3. fireAListenerEvent1fireBListenerEvent1方法类似的问题。

我正在使用 Java 1.5 版。

4

6 回答 6

13

我看不出BListener应该延长的理由AListener

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

您也不能添加addAListener(),因为派生类不能降低父类中存在的方法的可见性。此外,您不应该这样做,否则您将违反Liskov 替换原则(每个 B 都必须能够做 A 可以做的所有事情)。

最后一点,我会fire*()保护这些方法。通常根本没有理由让它们公开,减少公共成员的数量可以保持你的公共界面干净。

于 2008-12-16T08:30:24.820 回答
6

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

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

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

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

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

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

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

于 2009-01-29T15:54:48.220 回答
3

我从您对 sua 的评论中了解到,解雇 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)
  }

}

说明:管理监听器的代码是共享的,在基类监听器管理器中。由于泛型编译时检查,B 只能接收 BListener。触发 B 会自动触发 A。

于 2008-12-16T13:42:47.593 回答
3

在我看来,你可以让事情变得非常简单。

我的理解

  • 您有一个执行一些基本操作的基本类A

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

如果是这种情况,您需要处理这两个事件(basic用于 A 和basic + specific用于 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"

更进一步,您甚至可以摆脱接口以使其更简单

于 2009-01-29T03:50:19.110 回答
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);
    }
  }
  ...
}

正如我在评论中所说,我不确定你真正想要实现什么?从哪里调用谁,当它被调用时需要做什么?

于 2009-01-27T14:43:17.763 回答
1

A基于我们对&之间关系的了解很少B,我认为BListener制作AListener. 顾名思义,aBListener假设监听BEvents,它们已经是 s的子类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
于 2009-01-29T15:19:11.023 回答