Pregunta

Tengo una clase java que activa eventos java personalizados.La estructura del código es la siguiente:

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

Todo funciona correctamente, pero me gustaría crear una nueva subclase de A (llámela B), que puede desencadenar un nuevo evento.Estoy pensando en la siguiente modificación:

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

}

¿Es este el enfoque correcto?Estuve buscando ejemplos en la web, pero no pude encontrar ninguno.

Hay algunas cosas que no me gustan en esta solución:

  1. BListener tiene dos métodos que uno usa AEvent los otros usos BEvent como parámetro.
  2. B clase ambos tienen addAListener y addBListener métodos.¿Debo ocultar addAListener con una palabra clave privada? [ACTUALIZAR:no es posible ocultar con una palabra clave privada]
  3. Problema similar con fireAListenerEvent1 y fireBListenerEvent1 métodos.

Estoy usando Java versión 1.5.

¿Fue útil?

Solución

No veo una razón por la cual BListener debería extenderse AListener.

¿Realmente quieres obligar a todos los interesados ​​a B eventos para implementar también event1()?

Tampoco puedes agregar addAListener(), ya que una clase derivada no puede reducir la visibilidad de un método que está presente en la clase principal.Además, no debería necesitarlo, o violaría las Principio de sustitución de Liskov (cada B debe poder hacer todo lo que un A puede hacer).

Y como última observación, quisiera hacer la fire*() métodos protegidos.Por lo general, no hay ninguna razón para mantenerlos públicos y reducir el número de miembros públicos mantiene limpia la interfaz pública.

Otros consejos

No utilice sucesiones, que no es lo que quiere y va a llevar a una frágil y difícil de cambiar el diseño. La composición es una forma más flexible y un mejor enfoque para el diseño. Siempre trate de diseñar interfaces como granular como sea posible, ya que no debe cambiarse evento. Ellos son su contrato con el resto del sistema. Si la nueva funcionalidad hay que añadir la primera opción es añadir más información para el evento. Si ese no es el caso, entonces usted debe diseñar una nueva interfaz para la entrega de ese evento. Esto evita tener que modificar el código existente que no está afectada.

Esta es mi patrón favorito para esto, que creo que es comúnmente referido como un observador.

Hacer una nueva interfaz que define un métodos para ese tipo de evento (fooEvent () addFooEventListener () removeFooEventListener ()). Implementar esta interfaz en la clase concreta que genera estos eventos. (Por lo general llama a esto algo así como SourcesFooEvent, FiresFooEvent, FooEventSource, etc)

Si desea reducir la duplicación de código se puede construir una clase de ayuda, que maneja el registro de los oyentes, los almacena en una colección, y proporciona un método de fuego para la publicación de los acontecimientos.

Los genéricos puede ayudar aquí. En primer lugar, una interfaz de escucha genérica:

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

A continuación, una interfaz EventSource juego:

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

Por último una clase base abstracta para construir rápidamente una clase de ayuda para manejar el registro de los oyentes y el envío de sucesos:

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

Se podría hacer uso de la EventDispatcher abstracta a través de la encapsulación, permitiendo que cualquier otra clase para implementar fácilmente EventSource sin exigir que para otorgar ninguna clase en particular.

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

Esperamos que esto ilustra el buen equilibrio entre la seguridad de tipos (importante), la flexibilidad y la reutilización de código.

Entiendo de su comentario a saua que tiro B se disparará automáticamente una.

¿Por qué no utilizar un solo tipo de oyente y luego mezclar un poco de la herencia, la delegación y los genéricos?

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

}

Explicación: el código para la gestión de los oyentes es compartida, en la clase base Listener Manager. B sólo puede recibir BListeners a causa de los genéricos en tiempo de compilación de cheques. Disparando B se disparará automáticamente una.

Me parece que pueda mantener las cosas muy simples.

Mi understading

  • tiene una clase básica que realiza alguna Funcionamientobásico

  • Es posible que tenga una subclase más específica B que además pueden realizar otros specificOperations

Si ese es el caso, es necesario que se manejan ambos eventos ( básica para A y básica + específica para B)

Bueno, no es necesario sobrecargar los métodos para hacer eso, lo único que tiene que hacer es agregar controladores específicos (u oyentes) para eventos específicos.

Puede ser el caso de que el evento es "básica", que está muy bien.

Sin embargo, cuando el evento es específico, es necesario reaccionar en consecuencia. Por lo tanto, lo que haría es agregar un cheque el oyente específica para discriminar el específica evento como este:

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

Y eso es todo.

Su descripción del problema es demasiado abstracto, por lo que se pueden sugerir soluciones concretas. Sin embargo, si es difícil de explicar lo que quiere lograr, probablemente tendría que volver a analizar cuál es el problema en primer lugar.

Si mi entendimiento anterior es correcta (que necesita para manejar básica + específica algunas veces) el siguiente código de larga abajo puede ayudar.

Saludos


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

Resultado de muestra:

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"

Si lo toma aún más, incluso se puede deshacerse de las interfaces de mantenerlo simple

Si

public class BEvent extends AEvent {
...
}

public interface BListener extends AListener {

  public void event2(BEvent event);
}

¿No puede hacer algo como:

public class B extends A {

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

Como he dicho en el comentario, no estoy seguro de qué es lo que realmente quiere lograr? Quien es llamado desde dónde, y qué hay que hacer cuando te llaman?

Según la poca información que tenemos sobre la relación entre A & B, creo que es confuso hacer BListener una subinterfaz de AListener.Como sugiere el nombre, un BListener se supone que debe escuchar BEvents, que son ya una subclase de AEvents.Para mayor claridad, los oyentes deben tener propósitos perspicaces;no deberían superponerse innecesariamente.Además, no hay necesidad de oyentes superpuestos ya que ya defines métodos separados en la clase. B para manejar diferentes tipos de oyentes.

Para ilustrar mi punto, considere este ejemplo, diseñado según su código:

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)                                      

Este diseño funciona, pero es confuso porque ClickableMouseListener maneja dos tipos de eventos, y ClickableMouseWidget maneja dos tipos de oyentes, como bien ha señalado.Ahora, considere la siguiente alternativa que utiliza composición en lugar de herencia:

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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top