Pergunta

Eu tenho uma classe java que dispara eventos java personalizado. A estrutura do código é o seguinte:

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

Tudo funciona corretamente, mas eu gostaria de criar uma nova subclasse de A (chamá-lo B), que pode disparar um novo evento. Estou pensando na seguinte modificação:

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

}

Esta é a abordagem correta? Eu estava pesquisando na web para exemplos, mas não conseguiu encontrar nenhum.

Existem algumas coisas eu não gosto nesta solução:

  1. BListener tem dois métodos uma utilidades AEvent os outros usos BEvent como parâmetro.
  2. classe B tanto tem métodos addAListener e addBListener. Eu deveria me esconder addAListener com chave privada? [UPDATE: não é possível esconder com chave privada]
  3. problema semelhante com métodos fireAListenerEvent1 e fireBListenerEvent1.

Eu estou usando Java versão 1.5.

Foi útil?

Solução

Eu não vejo uma razão pela qual BListener deve estender AListener.

Você realmente quer à força todos os interessados ??em eventos B para também implementar event1()?

Além disso, você não pode adicionar addAListener(), já que uma classe derivada não pode reduzir a visibilidade de um método que está presente na classe pai. Além disso, você não precisa, ou você violaria a substituição de Liskov (cada B must ser capaz de fazer tudo o que um a pode fazer).

E como uma última observação, eu faria os métodos fire*() protegida. Geralmente não há razão para mantê-los pública e reduzir o número de membros públicos mantém sua interface pública limpo.

Outras dicas

Não use inheritence, não é o que você quer e vai levar a uma frágil e difícil de design de mudança. Composição é uma mais flexível e uma melhor abordagem para o design. Sempre tente criar interfaces como granular possível, porque eles não deve ser alterado evento. Eles são o seu contrato com o resto do sistema. Se novas necessidades de funcionalidade a ser adicionada a primeira opção é adicionar mais informações para o evento. Se isso não for o caso, então você deve criar uma nova interface para entregar esse evento. Isto evita ter que alterar o código existente que não é afetado.

Aqui está o meu padrão favorito para isso, eu acredito que é comumente referido como um Observer.

Faça uma nova interface que define um métodos para esse tipo de evento (fooEvent () addFooEventListener () removeFooEventListener ()). Implementar essa interface na classe concreta que gera esses eventos. (I normalmente chama esse algo como SourcesFooEvent, FiresFooEvent, FooEventSource, etc)

Se você quiser reduzir a duplicação de código que você pode construir uma classe auxiliar que lida com registro dos ouvintes, os armazena em uma coleção, e fornece um método de fogo para publicar os eventos.

Os genéricos podem ajudar aqui. Primeiro, uma interface ouvinte genérico:

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

Em seguida, uma interface EventSource de harmonização:

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

Finalmente uma classe base abstrata para construir rapidamente uma classe auxiliar para registro alça de ouvintes e expedição evento:

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

Você faria uso do EventDispatcher abstrato através de encapsulamento, permitindo que qualquer outra classe para implementar facilmente EventSource enquanto não a requerem para estender qualquer classe 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 este ilustra o bom equilíbrio entre a segurança tipo (importante), flexibilidade e reutilização de código.

Eu entendo de seu comentário a saua que disparando B dispara automaticamente A.

Por que não usar um único tipo de ouvinte e, em seguida, misturar um pouco de herança, delegação e 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)
  }

}

Explicação: o código para os ouvintes gestão é compartilhada, na classe base Listener Manager. B só pode receber BListeners por causa de genéricos em tempo de compilação verificação. Firing B dispara automaticamente A.

Parece-me que você pode manter as coisas muito simples.

Minha understading

  • Você tem uma classe básica A , que realiza alguma basicOperation

  • Você pode ter uma subclasse mais específica B que, além podem realizar mais specificOperations

Se for esse o caso, será necessário que ambos os eventos ser manipuladas ( básico para A e básico + específica para B)

Bem, você não precisa sobrecarregar os métodos para fazer isso, a única coisa que você precisa fazer é manipuladores add específicos (ou ouvintes) para eventos específicos.

Pode ser o caso, o evento é "básico", tudo bem.

Mas quando o evento é específico, você precisa reagir em conformidade. Então, o que eu gostaria de fazer é adicionar uma verificação em o ouvinte específico para discriminar o específica evento como este:

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

E é isso.

A sua descrição do problema é muito abstrata, por isso há soluções concretas podem ser sugeridas. No entanto, se é difícil explicar o que você quer alcançar, provavelmente você precisa re-analisar qual é o problema em primeiro lugar.

Se o meu entendimento acima está correta (que você precisa lidar com básico + específica algumas vezes) o seguinte código longa abaixo ajuda Maio.

Com os melhores cumprimentos


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

Exemplo de saída:

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"

Tomar-lo ainda mais, você ainda pode se livrar das interfaces para mantê-lo mais simples

Se

public class BEvent extends AEvent {
...
}

public interface BListener extends AListener {

  public void event2(BEvent event);
}

que você não pode fazer algo como:

public class B extends A {

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

Como eu disse no comentário, eu não tenho certeza do que você realmente deseja alcançar? Quem é chamado a partir de onde, eo que ele precisa fazer quando se obter chamado?

Com base no que pouca informação que temos sobre a relação entre A & B, eu acho que é confuso para fazer BListener uma subinterface de AListener. Como o nome sugere, um BListener é supor para ouvir BEvents, que são uma subclasse de AEvents. Para maior clareza, os ouvintes devem ter efeitos mais exigentes; eles não devem sobrepor-se desnecessariamente. Além disso, não há necessidade de tais ouvintes sobrepostas desde que você já definir métodos separados em B classe para lidar com diferentes tipos de ouvintes.

Para ilustrar meu ponto, considere este exemplo, denominado após seu 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 projeto funciona, mas é confuso porque alças ClickableMouseListener dois tipos de eventos, e lida com ClickableMouseWidget dois tipos de ouvintes, como você justamente salientou. Agora, considere o seguinte alternativa que usa a composição em vez de herança:

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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top