Наследование Слушателя Java

Вместо использования forEach используйте map и действуйте так, как будто аргументы, предоставленные функции, являются неизменяемыми. Это безумие, что вы делаете details.forEach((detail) => {...});, а затем назначаете detail с помощью detail.unitPrice = coefficient * detail.price;.

7
задан asalamon74 16 December 2008 в 12:57
поделиться

6 ответов

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

Вы действительно хотите вынудить всех заинтересованные B события, чтобы также реализовать event1()?

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

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

12
ответ дан 6 December 2019 в 06:51
поделиться

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

Вот мой любимый шаблон для этого, я полагаю, что он обычно упоминается как Наблюдатель.

Сделайте новый интерфейс, определяющий методы для того типа события (fooEvent () addFooEventListener () removeFooEventListener ()). Реализуйте этот интерфейс в реальном классе, который генерирует эти события. (Я обычно называю это чем-то как SourcesFooEvent, FiresFooEvent, FooEventSource, и т.д.),

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

Дженерики могут помочь здесь. Во-первых, универсальный интерфейс слушателя:

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

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

6
ответ дан 6 December 2019 в 06:51
поделиться

Я понимаю от Вашего комментария до 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)
  }

}

Объяснение: код для руководящих слушателей совместно используется в менеджере Слушателя базового класса. B может только получить BListeners из-за времени компиляции дженериков, проверив. Увольнение B автоматически запустит A.

3
ответ дан 6 December 2019 в 06:51
поделиться

Если

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

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

1
ответ дан 6 December 2019 в 06:51
поделиться

На основе того, между чем небольшой информацией мы имеем об отношениях 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
1
ответ дан 6 December 2019 в 06:51
поделиться

Это кажется мне, можно сохранить вещи очень простыми.

Мой understading

  • У Вас есть основной класс A, который выполняет некоторый basicOperation

  • У Вас может быть более определенный подкласс B, который, кроме того, может выполнить еще немного specificOperations

Если это так, Вам нужно это оба события быть обработанными (основной для 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"

Беря его далее, можно даже избавиться от интерфейсов для хранения его более простым

3
ответ дан 6 December 2019 в 06:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: