Un modèle d'observateur Générique En Java

les java.util.Observer et java.util.Observable sont laids. Ils nécessitent les sortes de moulages qui rendent les ventilateurs de sécurité inconfortables, et vous ne pouvez pas définir une classe comme étant un Observer de plusieurs choses sans moulages laids. En fait, dans" Comment puis-je savoir l'objet générique que la classe Observer envoie en Java? ", un répondeur dit: qu'un seul type de données doit être utilisé dans chaque observateur / observable.

j'essaie de faites une version générique du pattern observer en Java pour contourner ces deux problèmes. Ce n'est pas différent de celui du post mentionné précédemment, mais cette question n'a pas été évidemment résolue (Le Dernier commentaire est une question sans réponse de L'OP).

21
demandé sur Community 2012-11-13 18:37:12

6 réponses

je préfère utiliser une annotation pour qu'un auditeur puisse écouter différents types d'événements.

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

imprime

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);
            }
        }
    }
}
9
répondu Peter Lawrey 2012-11-13 15:02:31

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

espérons que cela sera utile à quelqu'un.

13
répondu Hbcdev 2012-11-13 14:52:39

Une mise à jour moderne: ReactiveX est une très bonne API pour la programmation asynchrone basé sur le pattern observer, et c'est totalement générique. Si vous utilisez Observer/Observable pour "streamer" des données ou des événements d'un endroit dans votre code à un autre, vous devriez certainement regarder dedans.

il est basé sur la programmation fonctionnelle, il semble donc très élégant avec la syntaxe lambda de Java 8:

Observable.from(Arrays.asList(1, 2, 3, 4, 5))
        .reduce((x, y) -> x + y)
        .map((v) -> "DecoratedValue: " + v)
        .subscribe(System.out::println);
4
répondu Lynn 2015-07-14 12:34:41

j'ai écrit une fois une implémentation générique du pattern observer pour Java en utilisant dynamic proxies . Voici un exemple de la façon dont il pourrait être utilisé:

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

le code source complet D'AbstractObservable est disponible sur pastebin . Je blogué sur la façon dont il fonctionne dans un peu plus de détails , se référant également à des projets connexes.

Jaana a écrit un intéressant résumé des différentes approches , contrastant également l'approche par procuration dynamique avec d'autres. Un grand merci bien sûr va à Allain Lalonde dont j'ai eu l'idée originale . Je n'ai toujours pas vérifié PerfectJPattern , mais il pourrait juste contenir une implémentation stable du pattern d'observateur ; au moins il semble comme une bibliothèque mature.

2
répondu Steven Jeuris 2013-07-25 22:58:51

essayez d'utiliser la classe EventBus De Goyave.

vous pouvez déclarer un observateur comme ceci:

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

de Nouvelles un EventBus comme ceci:

EventBus eventBus = new EventBus();

et enregistrer observateur comme ceci:

eventBus.register(new EventObserver());

dernière notification de L'observateur comme:

eventBus.post(message);
2
répondu Kedron 2014-03-27 08:54:39

j'ai trouvé une demande similaire mais c'était plutôt sur codereview. Je pense qu'il vaut la peine de le mentionner ici.

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

réponse originale de coderview stackexchange

0
répondu neo7 2018-01-25 23:38:42