Как объявлять и использовать события в Java
У меня простой класс-назовем его животным. Я хотел бы запустить событие в классе животных и обработать его в классе, где я создал экземпляр класса животных. В обработчике событий я хочу передать целое значение
Как я могу сделать что-то простое, как это?
9 ответов
когда вы хотите избежать наследования от java.утиль.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;
}
}
предполагая, что передаваемое целое число является частью состояния класса 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
перед его понижением, и трудно увидеть, какого типа события ожидает конкретная реализация наблюдателя, посмотрев на его определение интерфейса. Гораздо лучше определить конкретные реализации прослушивателя и события, чтобы избежать этого.
простой интерфейс событий выглядит так:
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
-создание класса должно сделать это:
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 чтобы знать, что я имею в виду.
посмотреть java.util.Observable
EDIT: Адамски PropertyChangeSupport
основанный подход кажется лучше наблюдаемым, который я предложил.
Я считаю, что самое простое решение из них было пропущено немного...
вам не нужно больше, чем это в 95% случаев:
public class Aminal extends Observable {
public void doSomethingThatNotifiesObservers() {
setChanged();
notifyObservers(new Integer(42));
}
}
Я предполагаю, что у вас не будет автоматического бокса, поэтому я сделал Integer
объект, но часть от этого,Observable
класс из JDK1.0 поэтому он должен присутствовать в Вашей версии Java.
С автоупаковка (в 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
извне перейдите к решению Стива Маклеода, но по моему опыту вы захотите, чтобы ваш класс обрабатывал то, что он знает, и позволял другим классам взаимодействовать с ним через Observer
интерфейсы.
единственное, что вам нужно знать, это то, что вам нужно позвонить setChanged()
до notifyObservers()
или Observable
не будет отправлять никаких событий.
Я думаю, это так, что вы можете назвать несколько setChanged()
а затем уведомить наблюдателей только один раз, давая им знать, что класс изменился и оставляя им решать, как это сделать.
это также хорошая форма, чтобы удалить наблюдателя, как только класс, где он был создан, больше не нужен, это для предотвращения наблюдатель, оставленный в наблюдаемом. Просто мусор, собирающий класс observer, не удалит его из наблюдаемого.
если вы хотите наблюдать за классом на основе каждого свойства, я бы рекомендовал пойти с PropertyChangeSupport
как описано выше Адамский. Есть также VetoableChangeSupport
это можно использовать, если вы хотите, чтобы слушатели могли блокировать изменения.
классы 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 {
}
моим первым предложением здесь было бы посмотреть AspectJ. Это один из шаблонов проектирования, который язык лучше всего обрабатывает. Следующая статья дает очень красноречивое описание того, как это может быть реализовано:
http://www.ibm.com/developerworks/java/library/j-aopwork6/index.html