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

}

これは正しいアプローチですか?Web でサンプルを検索しましたが、見つかりませんでした。

このソリューションには、気に入らない点がいくつかあります。

  1. BListenerには 2 つのメソッドがあり、一方はパラメーターとして使用しAEvent、もう一方は使用BEventします。
  2. BクラスにはaddAListeneraddBListenerメソッドの両方があります。プライベート キーワードで addAListener を非表示にする必要がありますか? [更新: プライベート キーワードで非表示にすることはできません]
  3. メソッドfireAListenerEvent1と同様の問題。fireBListenerEvent1

Java バージョン 1.5 を使用しています。

4

6 に答える 6

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

うまくいけば、これは型の安全性(重要)、柔軟性、コードの再利用の間の素晴らしいバランスを示しています。

于 2009-01-29T15:54:48.220 に答える
3

物事を非常にシンプルに保つことができるように思えます。

私の理解

  • いくつかの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<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

A&の関係について私たちが持っている情報がほとんどないことに基づいて、のサブインターフェイスBを作成するのは混乱していると思います。名前が示すように、aはすでにsのサブクラスであるsをリッスンすると想定されています。明確にするために、リスナーは識別力のある目的を持っている必要があります。それらは不必要に重ならないようにする必要があります。さらに、さまざまなタイプのリスナーを処理するためにクラスで個別のメソッドをすでに定義しているため、このような重複するリスナーは必要ありません。BListenerAListenerBListenerBEventAEventB

私のポイントを説明するために、コードに基づいてスタイル設定されたこの例を考えてみましょう。

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)                                      

この設計は機能しますが、ご指摘のとおり、ClickableMouseListener2種類のイベントを処理し、2種類のリスナーを処理するため、混乱を招きます。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 に答える
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 に答える