Frage

Ich habe eine Java-Klasse, die benutzerdefinierten Java-Ereignisse ausgelöst. Die Struktur des Codes ist die folgende:

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

Alles funktioniert einwandfrei, aber ich möchte eine neue Unterklasse von A erstellen (nennen wir es B), die ein neues Ereignis ausgelöst werden. Ich denke an die folgende Änderung:

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

}

Ist das der richtige Ansatz? Ich habe im Internet nach Beispielen gesucht, kann aber nicht finden.

Es gibt ein paar Dinge, Ich mag nicht in dieser Lösung:

  1. BListener hat zwei Methoden verwendet man AEvent die andere BEvent als Parameter verwendet.
  2. B Klasse beide hat addAListener und addBListener Methoden. Soll ich verstecken addAListener mit eigenem Stichwort? [UPDATE: es ist nicht möglich, mit einem eigenen Stichwort zu verstecken]
  3. Ein ähnliches Problem mit fireAListenerEvent1 und fireBListenerEvent1 Methoden.

Ich bin mit Java-Version 1.5.

War es hilfreich?

Lösung

Ich sehe keinen Grund, warum BListener sollte AListener verlängern.

Wollen Sie wirklich jeden zwingen Interesse an B Veranstaltungen auch event1() implementieren?

Sie können auch nicht addAListener() hinzufügen, da eine abgeleitete Klasse kann die Sichtbarkeit einer Methode nicht reduzieren, die in der übergeordneten Klasse vorhanden ist. Außerdem sollten Sie nicht brauchen, oder würden Sie die Liskov Substitutionsprinzip (alle B must verletzen der Lage sein, ein A zu tun alles tun kann).

Und als letzte Bemerkung, ich würde die fire*() Methoden geschützt werden. Normalerweise gibt es keinen Grund, überhaupt sie öffentlich zu halten und die Zahl der öffentlichen Mitglieder reduziert hält Ihre öffentliche Schnittstelle sauber.

Andere Tipps

Verwenden Sie inheritence nicht, es ist nicht das, was Sie wollen und werden zu einem spröden und schwierig führen Design zu ändern. Zusammensetzung ist eine flexiblere und ein besserer Ansatz für das Design. Versuchen Sie immer, Schnittstellen wie granular wie möglich zu gestalten, weil sie nicht geänderte Ereignis sein sollten. Sie sind Ihr Vertrag mit dem Rest des Systems. Wenn neue Funktionalität die erste Option hinzugefügt werden muss, ist, um weitere Informationen zu dem Ereignis hinzuzufügen. Wenn das nicht angemessen ist, dann sollten Sie eine neue Schnittstelle Entwurf für dieses Ereignis zu liefern. Dies verhindert, die alle vorhandenen Code zu ändern, die nicht betroffen ist.

Hier ist mein Lieblingsmuster für diese, ich glaube, es gemeinhin als Beobachter bezeichnet wird.

Erstellen Sie eine neue Schnittstelle ein Verfahren für diesen Ereignistyp (fooEvent () addFooEventListener () removeFooEventListener ()) zu definieren. Implementieren Sie diese Schnittstelle in der konkreten Klasse, die diese Ereignisse erzeugt. (I in der Regel nennt dies so etwas wie SourcesFooEvent, FiresFooEvent, FooEventSource usw.)

Wenn Sie Code-Duplizierung reduzieren Sie eine Hilfsklasse konstruieren können, die Registrierung der Hörer Griffe, speichert sie in einer Sammlung, und ein Feuer Methode für die Ereignisse zu veröffentlichen.

Generics kann hier helfen. Zunächst wird eine allgemeine Listener-Schnittstelle:

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

Als nächstes wird eine passende Schnittstelle Eventsource:

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

Schließlich eine abstrakte Basisklasse, um schnell eine Hilfsklasse zu konstruieren Registrierung von Hörern und Ereignis-Dispatch zu behandeln:

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

Sie würden die Verwendung des abstrakten Eventdispatcher durch Verkapselung machen, eine andere Klasse ermöglicht Eventsource leicht zu implementieren, während es nicht erfordern eine bestimmte Klasse zu erweitern.

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

Hoffentlich zeigt dies die gute Balance zwischen Typsicherheit (wichtig), Flexibilität und Wiederverwendung von Code.

ich von Ihrem Kommentar verstehen, dass Brennen B Saua wird A automatisch ausgelöst.

Warum nicht eine einzige Art von Hörer verwenden und dann eine Erbschaft, Delegation und Generika mischen?

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

}

Erläuterung: der Code für die Verwaltung von Hörern gemeinsam genutzt wird, in der Basisklasse Listener-Manager. B kann nur erhalten BListeners wegen Generika Kompilierung-Überprüfung. B Zündung automatisch ein Feuer.

Es scheint mir, Sie Dinge sehr einfach zu halten.

Mein understading

  • Sie haben eine Basisklasse A , das führt einige basicOperation

  • Sie eine spezifischere Unterklasse haben können B , dass zusätzlich durchführen kann etwas mehr specificOperations

Wenn das der Fall ist, müssen Sie, dass beiden Ereignisse behandelt werden ( Grund für A und Grund + spezifische für B)

Nun, Sie brauchen nicht die Methoden überlasten, das zu tun, das einzige, was Sie tun müssen, ist spezifische Handler (oder Hörer) für bestimmte Ereignisse hinzuzufügen.

Es kann der Fall sein, das Ereignis „basic“ ist, das ist in Ordnung.

Aber wenn das Ereignis spezifisch ist, müssen Sie entsprechend reagieren. Also, was ich tun würde, ist eine Überprüfung hinzufügen, um der spezifische Hörer der spezifische Veranstaltung wie diese zu unterscheiden:

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

Und das ist es.

Ihre Beschreibung des Problems ist zu abstrakt, so dass keine konkreten Lösungen vorgeschlagen werden können. Doch wenn es schwer zu erklären, was Sie erreichen wollen, wahrscheinlich müßten Sie erneut zu analysieren, was das Problem in erster Linie ist.

Wenn mein Verständnis über korrekt ist (die Sie behandeln müssen Grund + spezifische einige Male) der nach langen Code unten helfen kann.

Mit freundlichen Grüßen


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

Beispiel für die Ausgabe:

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"

es weiter machen, können Sie auch die Schnittstellen loszuwerden, es zu halten einfacher

Wenn

public class BEvent extends AEvent {
...
}

public interface BListener extends AListener {

  public void event2(BEvent event);
}

Sie können nicht tun somethin wie:

public class B extends A {

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

Wie ich im Kommentar gesagt habe, ich bin nicht sicher, was Sie eigentlich erreichen wollen? Wer heißt, von wo, und was es tun muss, wenn es aufgerufen werden?

Nach dem, was wenig Informationen, die wir haben über die Beziehung zwischen A & B, ich denke, es ist verwirrend BListener eine Subschnittstelle von AListener zu machen. Wie der Name schon sagt, ist ein BListener nehme an, für BEvents zu hören, die sind bereits eine Unterklasse von AEvents. Aus Gründen der Klarheit sollten die Zuhörer anspruchsvolle Zwecke haben; sie sollten nicht unnötig überlappen. Außerdem gibt es keine Notwendigkeit für eine solche überlappende Zuhörer, da Sie bereits verschiedene Methoden in der Klasse B definieren, um verschiedene Arten von Zuhörern zu behandeln.

Um meinen Punkt zu illustrieren, betrachten Sie dieses Beispiel, gestylt nach dem Code:

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)                                      

Dieser Entwurf funktioniert, aber es ist verwirrend, weil ClickableMouseListener zwei Arten von Veranstaltungen Griffe und ClickableMouseWidget behandelt zwei Arten von Zuhörern, wie Sie zu Recht hat darauf hingewiesen. Betrachten Sie nun die folgende Alternative, die Zusammensetzung statt Vererbung verwendet:

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
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top