Общий шаблон наблюдателя в Java
на java.util.Observer
и java.util.Observable
уродливы. Они требуют видов отливок, которые делают вентиляторы безопасности типов неудобными, и вы не можете определить класс как Observer
из нескольких вещей без уродливых слепков. На самом деле, в "как узнать общий объект, который класс Observer отправляет в Java?", качестве ответа говорит что в каждом наблюдателе / наблюдаемом должен использоваться только один тип данных.
Я пытаюсь сделать общую версию шаблона observer в Java, чтобы обойти обе эти проблемы. Это похоже на то, что было в ранее упомянутом посте, но этот вопрос не был явно решен (последний комментарий-вопрос без ответа от OP).
6 ответов
Я предпочитаю использовать аннотацию, чтобы слушатель мог слушать различные типы событий.
public class BrokerTestMain {
public static void main(String... args) {
Broker broker = new Broker();
broker.add(new Component());
broker.publish("Hello");
broker.publish(new Date());
broker.publish(3.1415);
}
}
class Component {
@Subscription
public void onString(String s) {
System.out.println("String - " + s);
}
@Subscription
public void onDate(Date d) {
System.out.println("Date - " + d);
}
@Subscription
public void onDouble(Double d) {
System.out.println("Double - " + d);
}
}
печать
String - Hello
Date - Tue Nov 13 15:01:09 GMT 2012
Double - 3.1415
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Subscription {
}
public class Broker {
private final Map<Class, List<SubscriberInfo>> map = new LinkedHashMap<Class, List<SubscriberInfo>>();
public void add(Object o) {
for (Method method : o.getClass().getMethods()) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (method.getAnnotation(Subscription.class) == null || parameterTypes.length != 1) continue;
Class subscribeTo = parameterTypes[0];
List<SubscriberInfo> subscriberInfos = map.get(subscribeTo);
if (subscriberInfos == null)
map.put(subscribeTo, subscriberInfos = new ArrayList<SubscriberInfo>());
subscriberInfos.add(new SubscriberInfo(method, o));
}
}
public void remove(Object o) {
for (List<SubscriberInfo> subscriberInfos : map.values()) {
for (int i = subscriberInfos.size() - 1; i >= 0; i--)
if (subscriberInfos.get(i).object == o)
subscriberInfos.remove(i);
}
}
public int publish(Object o) {
List<SubscriberInfo> subscriberInfos = map.get(o.getClass());
if (subscriberInfos == null) return 0;
int count = 0;
for (SubscriberInfo subscriberInfo : subscriberInfos) {
subscriberInfo.invoke(o);
count++;
}
return count;
}
static class SubscriberInfo {
final Method method;
final Object object;
SubscriberInfo(Method method, Object object) {
this.method = method;
this.object = object;
}
void invoke(Object o) {
try {
method.invoke(object, o);
} catch (Exception e) {
throw new AssertionError(e);
}
}
}
}
наблюдателя.java
package util;
public interface Observer<ObservedType> {
public void update(Observable<ObservedType> object, ObservedType data);
}
наблюдаема.java
package util;
import java.util.LinkedList;
import java.util.List;
public class Observable<ObservedType> {
private List<Observer<ObservedType>> _observers =
new LinkedList<Observer<ObservedType>>();
public void addObserver(Observer<ObservedType> obs) {
if (obs == null) {
throw new IllegalArgumentException("Tried
to add a null observer");
}
if (_observers.contains(obs)) {
return;
}
_observers.add(obs);
}
public void notifyObservers(ObservedType data) {
for (Observer<ObservedType> obs : _observers) {
obs.update(this, data);
}
}
}
надеюсь, это будет кому-то полезным.
современное обновление: ReactiveX - очень хороший API для асинхронного программирования на основе шаблона Observer, и он полностью общий. Если вы используете Observer / Observable для "потоковой передачи" данных или событий из одного места в коде в другое, вы обязательно должны изучить его.
Он основан на функциональном программировании, поэтому он выглядит очень гладким с лямбда-синтаксисом Java 8:
Observable.from(Arrays.asList(1, 2, 3, 4, 5))
.reduce((x, y) -> x + y)
.map((v) -> "DecoratedValue: " + v)
.subscribe(System.out::println);
Я однажды написал общую реализацию шаблона observer для Java, используя динамические прокси. Вот пример того, как его можно использовать:
Gru gru = new Gru();
Minion fred = new Minion();
fred.addObserver(gru);
fred.moo();
public interface IMinionListener
{
public void laughing(Minion minion);
}
public class Minion extends AbstractObservable<IMinionListener>
{
public void moo()
{
getEventDispatcher().laughing(this);
}
}
public class Gru implements IMinionListener
{
public void punch(Minion minion) { ... }
public void laughing(Minion minion)
{
punch(minion);
}
}
на полный исходный код AbstractObservable доступен на pastebin. Назад я блог о том, как это работает в немного более подробно, также ссылаясь на родственные проекты.
Яана написал интересное резюме различных подходов, также контрастные динамический прокси-подход с другими. Большое спасибо конечно идет Аллену Лалонду, от которого я получил оригинальную идею. Я все еще не выписался PerfectJPattern, но он может просто содержать стабильная реализация шаблона наблюдателя; по крайней мере, это похоже на зрелую библиотеку.
попробуйте использовать класс EventBus Guava.
вы можете объявить наблюдателя так:
public class EventObserver {
@Subscribe
public void onMessage(Message message) {
...
}
}
новый EventBus, как это:
EventBus eventBus = new EventBus();
и зарегистрируйте наблюдателя так:
eventBus.register(new EventObserver());
последнее уведомление наблюдателя, как:
eventBus.post(message);
Я нашел аналогичный запрос, но он был скорее на codereview. Думаю, стоит упомянуть об этом здесь.
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Supplier;
/**
* like java.util.Observable, But uses generics to avoid need for a cast.
*
* For any un-documented variable, parameter or method, see java.util.Observable
*/
public class Observable<T> {
public interface Observer<U> {
public void update(Observable<? extends U> observer, U arg);
}
private boolean changed = false;
private final Collection<Observer<? super T>> observers;
public Observable() {
this(ArrayList::new);
}
public Observable(Supplier<Collection<Observer<? super T>>> supplier) {
observers = supplier.get();
}
public void addObserver(final Observer<? super T> observer) {
synchronized (observers) {
if (!observers.contains(observer)) {
observers.add(observer);
}
}
}
public void removeObserver(final Observer<? super T> observer) {
synchronized (observers) {
observers.remove(observer);
}
}
public void clearObservers() {
synchronized (observers) {
this.observers.clear();
}
}
public void setChanged() {
synchronized (observers) {
this.changed = true;
}
}
public void clearChanged() {
synchronized (observers) {
this.changed = false;
}
}
public boolean hasChanged() {
synchronized (observers) {
return this.changed;
}
}
public int countObservers() {
synchronized (observers) {
return observers.size();
}
}
public void notifyObservers() {
notifyObservers(null);
}
public void notifyObservers(final T value) {
ArrayList<Observer<? super T>> toNotify = null;
synchronized(observers) {
if (!changed) {
return;
}
toNotify = new ArrayList<>(observers);
changed = false;
}
for (Observer<? super T> observer : toNotify) {
observer.update(this, value);
}
}
}