Java. Корректный шаблон для реализации слушателей

Очень обычно у меня есть ситуация, где данный объект должен будет иметь многих слушателей. Например, я мог бы иметь

class Elephant {
  public void addListener( ElephantListener listener ) { ... }
}

но у меня будет много таких ситуаций. Таким образом, у меня также будет a Tiger объект это будет иметь TigerListeners. Теперь, TigerListeners и ElephantListeners очень отличаются:

interface TigerListener {
  void listenForGrowl( Growl qrowl );
  void listenForMeow( Meow meow );
}

в то время как

interface ElephantListener {
  void listenForStomp( String location, double intensity );
}

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

25
задан Jake 4 June 2010 в 16:19
поделиться

3 ответа

Я думаю, вы делаете это правильно, поскольку ваши интерфейсы имеют семантическую ценность и выражают то, что они слушают (например, рычание и мяуканье вместо топания). При общем подходе вы можете повторно использовать широковещательный код, но вы можете потерять удобочитаемость.

Например, существует java.beans.PropertyChangeSupport , которая представляет собой служебную программу для реализации Оберсервера, отслеживающего изменения значений. Он выполняет широковещательную передачу, но вам все равно необходимо реализовать метод в своем доменном классе и передать его объекту PropertyChangeSupport. Методы обратного вызова сами по себе бессмысленны, а транслируемые события основаны на String:

public interface PropertyChangeListener extends java.util.EventListener {
     void propertyChange(PropertyChangeEvent evt);
}

Другой - java.util.Observable , который обеспечивает механизм широковещания, но это тоже не самое лучшее, imho.

Мне нравится ElephantListener.onStomp ()

3
ответ дан 28 November 2019 в 21:05
поделиться

Вместо того, чтобы каждый Listener имел определенные методы для каждого типа события, вы можете его отправить, измените интерфейс, чтобы он принимал общий класс Event . Затем вы можете подклассифицировать Событие для определенных подтипов, если вам нужно, или сделать так, чтобы оно содержало состояние, такое как , двойная интенсивность .

TigerListener и ElephentListener затем становятся

interface TigerListener {
    void listen(Event event);
}

Фактически, затем вы можете дополнительно преобразовать этот интерфейс в простой Listener :

interface Listener {
    void listen(Event event);
}

Ваши реализации Listener могут затем содержать логику, которая они нужны для конкретных событий, о которых они заботятся

class TigerListener implements Listener {
    @Overrides
    void listen(Event event) {
        if (event instanceof GrowlEvent) {
            //handle growl...
        }
        else if (event instance of MeowEvent) {
            //handle meow
        }
        //we don't care about any other types of Events
    }
}

class ElephentListener {
    @Overrides
    void listen(Event event) {
        if (event instanceof StompEvent) {
            StompEvent stomp = (StompEvent) event;
            if ("north".equals(stomp.getLocation()) && stomp.getDistance() > 10) { 
                ... 
            }
        }
    }
}

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

28
ответ дан 28 November 2019 в 21:05
поделиться

Другой вариант - Whiteboard Pattern. В этом случае издатель и подписчик отделены друг от друга, и ни один из них не будет содержать никакого широковещательного кода. Они оба просто используют механизм обмена сообщениями для pub/sub, и ни один из них не имеет прямой связи с другим.

Это обычная модель для обмена сообщениями в платформе OSGi.

1
ответ дан 28 November 2019 в 21:05
поделиться
Другие вопросы по тегам:

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