Вопрос

У меня есть 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 имеет два метода, которые каждый использует AEvent другие виды использования BEvent в качестве параметра.
  2. B класс обоих имеет addAListener и addBListener методы.Должен ли я скрыть addAListener с помощью ключевого слова private? [ОБНОВИТЬ:это невозможно скрыть с помощью ключевого слова private]
  3. Аналогичная проблема с fireAListenerEvent1 и fireBListenerEvent1 методы.

Я использую Java версии 1.5.

Это было полезно?

Решение

Я не вижу причины, почему BListener следует расширять AListener.

Вы действительно хотите заставить всех интересоваться B мероприятия по также реализации event1()?

Также вы не можете добавить addAListener(), поскольку производный класс не может уменьшить видимость метода, который присутствует в родительском классе.Кроме того, вам не должно быть необходимости в этом, иначе вы нарушили бы Принцип замещения Лискова (каждый B должен уметь делать все, что может делать A).

И в качестве последнего замечания я бы сделал следующее fire*() методы защищены.Обычно нет никаких причин делать их общедоступными, а сокращение количества общедоступных участников обеспечивает чистоту вашего общедоступного интерфейса.

Другие советы

Не используйте наследование, это не то, что вы хотите, и приведет к хрупкому и трудноизменяемому дизайну.Композиция - это более гибкий и совершенный подход к дизайну.Всегда старайтесь разрабатывать интерфейсы как можно более детализированными, потому что они не должны изменяться в случае необходимости.Это ваш контракт с остальной системой.Если необходимо добавить новую функциональность, первый вариант - добавить дополнительную информацию к событию.Если это неуместно, то вам следует разработать новый интерфейс для проведения этого мероприятия.Это предотвращает необходимость изменять любой существующий код, который не затронут.

Вот мой любимый шаблон для этого, я полагаю, его обычно называют Наблюдателем.

Создайте новый интерфейс, определяющий методы для этого типа события (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);
  }
}

Надеюсь, это иллюстрирует хороший баланс между безопасностью типов (важно), гибкостью и повторным использованием кода.

Из вашего комментария saua я понял, что запуск 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)
  }

}

Объяснение:код для управления прослушивателями является общим в базовом классе Listener Manager.B может получать BListeners только из-за проверки generics во время компиляции.Включение B автоматически приведет к срабатыванию A.

Мне кажется, вы можете сделать все очень просто.

Мое понимание

  • У вас есть базовый класс 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"

Идя дальше, вы даже можете избавиться от интерфейсов, чтобы упростить их

Если

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

Как я уже сказал в комментарии, я не уверен, чего вы на самом деле хотите достичь?Кто откуда вызывается и что ему нужно сделать, когда его вызовут?

Основываясь на той скудной информации, которой мы располагаем об отношениях между A & B, я думаю, что это сбивает с толку, чтобы сделать BListener подинтерфейс AListener.Как следует из названия, a BListener предполагается, что нужно прислушиваться к BEvents, которые являются уже подкласс AEvents.Для ясности у слушателей должны быть проницательные цели;они не должны перекрываться без необходимости.Кроме того, нет необходимости в таких перекрывающихся прослушивателях, поскольку вы уже определяете отдельные методы в классе 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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top