У меня есть простой класс - назовем его Animal. Я хотел бы запустить событие в классе Animal и обработать его в классе, где я создал экземпляр класса Animal. В обработчике событий я хочу передать значение Integer
Как я могу сделать что-то простое?
У меня есть простой класс - назовем его Animal. Я хотел бы запустить событие в классе Animal и обработать его в классе, где я создал экземпляр класса Animal. В обработчике событий я хочу передать значение Integer
Как я могу сделать что-то простое?
Если вы хотите избежать наследования из базового класса 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;
}
}
Предполагая, что переданное целое является частью состояния класса 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, посмотрев на определение интерфейса. Намного лучше определить конкретные реализации и события слушателя, чтобы избежать этого.
Простой интерфейс событий выглядит следующим образом:
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);
.EDIT: Адамски PropertyChangeSupport
подход кажется лучше. Наблюдается тот, который я предложил.
Я считаю, что простейшее их решение было пропущено немного...
Вам не нужно больше, чем это, в 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
, который вы можете использовать, если хотите, чтобы слушатели могли заблокировать изменение.
Классы 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
Существуют также большие библиотеки Spring, которые предоставляют встроенную инфраструктуру событий .
Guava EventBus - еще одна альтернативная версия