24

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

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

4

6 回答 6

16

观察者.java

package util;

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

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

希望这对某人有用。

于 2012-11-13T14:37:12.617 回答
12

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

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);
            }
        }
    }
}
于 2012-11-13T15:02:31.947 回答
5

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

它基于函数式编程,因此使用 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);
于 2015-07-14T12:34:41.267 回答
3

我曾经使用动态代理为 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 上找到。回想起来,我在博客上更详细地介绍了它的工作原理,还提到了相关项目。

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

于 2013-07-25T22:58:51.983 回答
3

尝试使用 Guava 的 EventBus 类。

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

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

像这样新建一个 EventBus:

EventBus eventBus = new EventBus();

并像这样注册观察者:

eventBus.register(new EventObserver());

最后通知观察者,如:

eventBus.post(message);
于 2014-03-27T08:54:39.310 回答
0

我发现了一个类似的请求,但它是在 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);
        }
    }
}

来自 codereview stackexchange 的原始答案

于 2018-01-25T23:38:42.097 回答