简体   繁体   English

Java中的通用观察者模式

[英]A generic observer pattern in Java

The java.util.Observer and java.util.Observable are ugly. java.util.Observerjava.util.Observable很难看。 They require the sorts of casts that make type-safety fans uncomfortable, and you can't define a class to be an Observer of multiple things without ugly casts. 它们需要使类型安全风扇不舒服的各种类型的转换,并且你不能将类定义为多个事物的Observer而没有丑陋的转换。 In fact, in " How do I know the generic object that the Observer class sends in Java? ", an answerer says that only one type of data should be used in each observer / observable. 事实上,在“ 我如何知道Observer类在Java中发送的通用对象? ”中, 应答者说每个观察者/可观察者中只应使用一种类型的数据。

I'm trying to make a generic version of the observer pattern in Java to get round both these problems. 我正在尝试在Java中创建一个通用版本的观察者模式来解决这两个问题。 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). 它与之前提到的帖子没有什么不同,但这个问题没有明显解决(最后一个评论是来自OP的未回答的问题)。

Observer.java Observer.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. 希望这对某人有用。

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

A modern update: ReactiveX is a very nice API for asynchronous programming based on the Observer pattern, and it's fully generic. 一个现代的更新: ReactiveX是一个非常好的基于Observer模式的异步编程API,它是完全通用的。 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. 如果您使用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);

I once wrote a generic implementation of the observer pattern for Java using dynamic proxies . 我曾经使用动态代理编写了Java的观察者模式的通用实现。 Here's a sample of how it could be used: 以下是如何使用它的示例:

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 . 在pastebin上可以获得AbstractObservable完整源代码 Way back I blogged about how it works in a bit more detail , also referring to related projects. 回过头来,我在博客中详细介绍了它的工作原理 ,并参考了相关项目。

Jaana wrote an interesting summary of different approaches , also contrasting the dynamic proxy approach with others. Jaana写了一个关于不同方法的有趣总结 ,也将动态代理方法与其他方法进行了对比。 Much thanks of course goes to Allain Lalonde from which I got the original idea . 非常感谢Allain Lalonde,我从中得到了最初的想法 I still haven't checked out PerfectJPattern , but it might just contain a stable implementation of the observer pattern ; 我还没有检查过PerfectJPattern ,但它可能只包含一个稳定的观察者模式实现 ; at least it seems like a mature library. 至少它看起来像一个成熟的图书馆。

Try to use class EventBus of Guava. 尝试使用Guava的类EventBus。

You can declare a Observer like this: 你可以像这样声明一个Observer:

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

New a EventBus like this: 新增一个像这样的EventBus:

EventBus eventBus = new EventBus();

And register Observer like this: 并注册Observer,如下所示:

eventBus.register(new EventObserver());

Last notify Observer like: 最后通知Observer如:

eventBus.post(message);

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的原始答案

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM