Java 中的通用观察者模式

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13362636/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 12:26:25  来源:igfitidea点击:

A generic observer pattern in Java

javagenericsobserver-pattern

提问by hcarver

The java.util.Observerand java.util.Observableare ugly. They require the sorts of casts that make type-safety fans uncomfortable, and you can't define a class to be an Observerof multiple things without ugly casts. In fact, in "How do I know the generic object that the Observer class sends in Java?", an answerer saysthat only one type of data should be used in each observer / observable.

java.util.Observerjava.util.Observable丑陋的。他们需要那种让类型安全爱好者不舒服的类型转换,而且你不能定义一个类是Observer多个事物的一个没有丑陋的转换。事实上,在“我如何知道 Java 中 Observer 类发送的通用对象?”中,一位回答者说每个观察者/可观察对象中只应使用一种类型的数据。

I'm trying to make a generic version of the observer pattern in Java to get round both these problems. It's not unlike the one in the previously mentioned post, but that question was not obviously resolved (the last comment is an unanswered question from the OP).

我正在尝试在 Java 中制作观察者模式的通用版本来解决这两个问题。它与前面提到的帖子中的那个没有什么不同,但是这个问题并没有明显解决(最后一条评论是来自 OP 的一个未回答的问题)。

采纳答案by Peter Lawrey

I prefer using an annotation so a listener can listen to different types of events.

我更喜欢使用注释,以便侦听器可以侦听不同类型的事件。

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);
    }
}

prints

印刷

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);
            }
        }
    }
}

回答by hcarver

Observer.java

观察者.java

package util;

public interface Observer<ObservedType> {
    public void update(Observable<ObservedType> object, ObservedType data);
}

Observable.java

Observable.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);
        }
    }
}

Hopefully this will be useful to someone.

希望这对某人有用。

回答by Lynn

A modern update:ReactiveXis a very nice API for asynchronous programming based on the Observer pattern, and it's fully generic. If you're using Observer/Observable to "stream" data or events from one place in your code to another, you should definitely look into it.

现代更新:ReactiveX是一个非常好的基于观察者模式的异步编程 API,它是完全通用的。如果您使用 Observer/Observable 将数据或事件从代码中的一个位置“流式传输”到另一个位置,那么您绝对应该研究一下。

It's based on functional programming, so it looks very sleek with Java 8's lambda syntax:

它基于函数式编程,因此使用 Java 8 的 lambda 语法看起来非常时尚:

Observable.from(Arrays.asList(1, 2, 3, 4, 5))
        .reduce((x, y) -> x + y)
        .map((v) -> "DecoratedValue: " + v)
        .subscribe(System.out::println);

回答by Steven Jeuris

I once wrote a generic implementation of the observer pattern for Java using dynamic proxies. Here's a sample of how it could be used:

我曾经使用动态代理为 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);
    }
}

The full source code of AbstractObservable is available on pastebin. Way back I blogged about how it works in a bit more detail, also referring to related projects.

AbstractObservable完整源代码可在 pastebin 上获得。回想起来,我在博客上更详细地介绍了它的工作原理,还参考了相关项目。

Jaana wrote an interesting summary of different approaches, also contrasting the dynamic proxy approach with others. Much thanks of course goes to Allain Lalonde from which I got the original idea. I still haven't checked out PerfectJPattern, but it might just contain a stable implementation of the observer pattern; at least it seems like a mature library.

Jaana 写了一篇关于不同方法的有趣总结,还将动态代理方法与其他方法进行了对比。非常感谢Alllain Lalonde,我从中得到了最初的想法。我仍然没有检查PerfectJPattern,但它可能只包含观察者模式的稳定实现;至少它看起来像一个成熟的图书馆。

回答by Kedron

Try to use class EventBus of Guava.

尝试使用 Guava 的 EventBus 类。

You can declare a Observer like this:

你可以像这样声明一个观察者:

    public class EventObserver {
        @Subscribe 
        public void onMessage(Message message) {
            ...
        }
    }

New a EventBus like this:

像这样新建一个 EventBus:

EventBus eventBus = new EventBus();

And register Observer like this:

并像这样注册观察者:

eventBus.register(new EventObserver());

Last notify Observer like:

最后通知观察者,如:

eventBus.post(message);

回答by neo7

I found a similar request but it was rather on codereview. I think it's worth mentioning it here.

我发现了一个类似的请求,但它是在代码中。我认为这里值得一提。

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);
        }
    }
}

Original answer from codereview stackexchange

来自 codereview stackexchange 的原始答案