Подтвердить что ты не робот

Как объявлять и потреблять события в Java

У меня есть простой класс - назовем его Animal. Я хотел бы запустить событие в классе Animal и обработать его в классе, где я создал экземпляр класса Animal. В обработчике событий я хочу передать значение Integer

Как я могу сделать что-то простое?

4b9b3361

Ответ 1

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

Вот наблюдаемый интерфейс:

public interface IObservable
{
        void addObserver(IObserver o);

        void deleteObserver(IObserver o);

        void notifyObservers(INotification notification);
}

Вот вспомогательный класс, который может использоваться вашими реальными наблюдаемыми:

import java.util.ArrayList;
import java.util.List;


public class Observable implements IObservable
{
        private List<IObserver> observers;

        @Override
        public synchronized void addObserver(IObserver o)
        {
                if (observers == null)
                {
                        observers = new ArrayList<IObserver>();
                }
                else if (observers.contains(o))
                {
                        return;
                }
                observers.add(o);
        }

        @Override
        public synchronized void deleteObserver(IObserver o)
        {
                if (observers == null)
                {
                        return;
                }
                int idx = observers.indexOf(o);
                if (idx != -1)
                {
                        observers.remove(idx);
                }
        }

        @Override
        public synchronized void notifyObservers(INotification notification)
        {
                if (observers == null)
                {
                        return;
                }
                for (IObserver o : observers)
                {
                        o.update(notification);
                }
        }

}

Реальный наблюдаемый может выглядеть так:

class Person implements IObservable
{
        private final IObservable observable = new Observable();

        @Override
        public void setFirstName(String firstName) throws Exception
        {
            if (firstName == null || firstName.isEmpty())
            {
                    throw new Exception("First name not set");
            }

            this.firstName = firstName;
            notifyObservers(new Notification(this, getFirstNamePropertyId()));
        }

    @Override
    public void addObserver(IObserver o)
    {
            observable.addObserver(o);
    }

    @Override
    public void deleteObserver(IObserver o)
    {
            observable.deleteObserver(o);
    }

    @Override
    public void notifyObservers(INotification notification)
    {
            observable.notifyObservers(notification);
    }

    private static final String FIRSTNAME_PROPERTY_ID = "Person.FirstName";

    @Override
    public String getFirstNamePropertyId()
    {
            return FIRSTNAME_PROPERTY_ID;
    }

}

Вот интерфейс наблюдателя:

public interface IObserver
{
        void update(INotification notification);
}

Наконец, вот интерфейс уведомления и базовая реализация:

public interface INotification
{
        Object getObject();

        Object getPropertyId();
}

public class Notification implements INotification
{
        private final Object object;
        private final Object propertyId;

        public Notification(Object object, Object propertyId)
        {
                this.object = object;
                this.propertyId = propertyId;
        }

        @Override
        public Object getObject()
        {
                return object;
        }

        @Override
        public Object getPropertyId()
        {
                return propertyId;
        }
}

Ответ 2

Предполагая, что переданное целое является частью состояния класса Animal, идиоматический способ сделать это , а не писать много собственного кода, - это запустить PropertyChangeEvent. Вы можете использовать класс PropertyChangeSupport для этого, уменьшив код до этого:

public class Animal {
  // Create PropertyChangeSupport to manage listeners and fire events.
  private final PropertyChangeSupport support = new PropertyChangeSupport(this);
  private int foo;

  // Provide delegating methods to add / remove listeners to / from the support class.  
  public void addPropertyChangeListener(PropertyChangeListener l) {
    support.addPropertyChangeListener(l);
  }

  public void removePropertyChangeListener(PropertyChangeListener l) {
    support.removePropertyChangeListener(l);
  }

  // Simple example of how to fire an event when the value of 'foo' is changed.
  protected void setFoo(int foo) {
    if (this.foo != foo) {
      // Remember previous value, assign new value and then fire event.
      int oldFoo = this.foo;
      this.foo = foo;
      support.firePropertyChange("foo", oldFoo, this.foo);
    }
  }
}

Наконец, я бы посоветовал использовать Observer/Observable, поскольку он делает код нечитаемым/трудным для выполнения: вам постоянно нужно проверять тип аргумента, переданного в Observer с помощью instanceof до его понижения, и трудно понять, какой тип события ожидает конкретная реализация Observer, посмотрев на определение интерфейса. Намного лучше определить конкретные реализации и события слушателя, чтобы избежать этого.

Ответ 3

Простой интерфейс событий выглядит следующим образом:

public interface AnimalListener {
    public void animalDoesSomething(int action);
}

Animal должен управлять своими слушателями:

public class Animal {
    private final List<AnimalListener> animalListeners = new ArrayList<AnimalListener>()
    public void addAnimalListener(AnimalListener animalListener) {
        animalListeners.add(animalListener);
    }
}

Ваш класс Animal -creating должен сделать это:

public class AnimalCreator implements AnimalListener {
    public void createAnimal() {
        Animal animal = new Animal();
        animal.addAnimalListener(this); // implement addListener in An
    }
    public void animalDoesSomething(int action) {
        System.ot.println("Holy crap, animal did something!");
    }
}

Теперь Animal может запускать события.

public class Animal {
    ....
    public void doSomething() {
        for (AnimalListener animalListener : animalListeners) {
            animalListener.animalDoesSomething(4);
        }
    }
}

Это похоже на много кода для чего-то столь же простого, как "стрельба", но, возможно, запуск стрельбы не является простым.:)

Конечно, существуют различные расширения этого простого механизма.

  • Я всегда делаю, чтобы мои слушатели событий расширяли java.util.EventListener.
  • Первым параметром для каждого метода слушателя должен быть источник события, т.е. public void animalDoesSomething(Animal animal, int action);.
  • Управление зарегистрированными слушателями и сжигание событий можно абстрагировать до какого-либо класса управления прослушивателем абстрактных событий. Посмотрите PropertyChangeSupport, чтобы узнать, что я имею в виду.

Ответ 5

Я считаю, что простейшее их решение было пропущено немного...

Вам не нужно больше, чем это, в 95% случаев:

public class Aminal extends Observable {
    public void doSomethingThatNotifiesObservers() {
        setChanged();
        notifyObservers(new Integer(42));
    }
}

Я предполагаю, что у вас не будет автоматического бокса, поэтому я создал объект Integer, но часть от него, класс Observable - от JDK1.0, поэтому он должен присутствовать в вашей версии Java.

С autoboxing (в Java 1.5 и более поздних версиях) вызов notifyObservers будет выглядеть следующим образом:

notifyObservers(42);

Чтобы поймать отправленное событие, вам нужно реализовать интерфейс Observer:

public class GetInt implements Observer {
    @Override
    public void update(Observable o, Object arg) {
        if (arg instanceof Integer) {
            Integer integer = (Integer)arg;
            // do something with integer
        }
    }
}

Затем вы добавите GetInt в класс Animal:

Animal animal = new Animal();
GetInt getint = new GetInt();
animal.addObserver(getint);

Это стандартная Java, а класс Observable реализует всю необходимую обработку наблюдателя.

Если вам нужно запускать Observable извне, переходите к решению Steve McLeod, но, по моему опыту, вы хотите, чтобы ваш класс обрабатывал то, что он знал, и позволял другим классам взаимодействовать с ним через Observer интерфейсов.

Единственное, что вам нужно знать, это то, что вам нужно позвонить setChanged(), прежде чем notifyObservers() или Observable не отправит никаких событий.

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

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

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

Ответ 6

Классы Observer/Observable находятся на Java с 1-го дня. К сожалению, оригинальные дизайнеры немного прищурились. Справедливости ради, у них не было возможности учиться на 10-летнем опыте Java...

Я решаю вашу проблему с делегацией. У меня есть собственная реализация Observer/Observable - и я рекомендую это. Но вот подход, который работает:

import java.util.Observable;
import java.util.Observer;

public class Animal {

    private final ImprovedObservable observable = new ImprovedObservable();

    public void addObserver(Observer o) {
        observable.addObserver(o);
    }

    public void notifyObservers() {
        observable.notifyObservers();
    }

    public void doSomething() {
        observable.setChanged();
        observable.notifyObservers(new AnimalEvent());
    }


}

// simply make setChanged public, and therefore usable in delegation
class ImprovedObservable extends Observable {
    @Override
    public void setChanged() {
        super.setChanged();
    }
}

class AnimalEvent {
}

Ответ 7

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

http://www.ibm.com/developerworks/java/library/j-aopwork6/index.html

Ответ 8

Существуют также большие библиотеки Spring, которые предоставляют встроенную инфраструктуру событий .

Ответ 9

Guava EventBus - еще одна альтернативная версия