Soru Java'da genel bir gözlemci deseni


java.util.Observer ve java.util.Observable çirkin mi. Tip güvenliği hayranlarını rahatsız eden çeşitli türlere ihtiyaç duyuyorlar. Observer çirkin oyuncular olmadan çok şeylerin. Aslında, "Observer sınıfının Java'da gönderdiği genel nesneyi nasıl bilebilirim?" bir cevap diyor Her gözlemciye sadece bir tür veri kullanılmalı / gözlemlenebilir.

Bu sorunları çözmek için Java'daki gözlemci şablonunun genel bir sürümünü oluşturmaya çalışıyorum. Bu, daha önce bahsedilen yazıdan farklı değil, fakat bu soru açıkça çözülmedi (son yorum, OP'den cevapsız bir sorudur).


21
2017-11-13 14:37


Menşei


Son satırdaki eksik link? - Ed Staub
Bunun imkansız olduğundan şüpheleniyorum temizce bir dil uzantısı olmadan. - immibis


Cevaplar:


Bir ek açıklama kullanmayı tercih ederim, bu yüzden dinleyici farklı türdeki olayları dinleyebilir.

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

baskılar

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
2017-11-13 15:02



Bu kesinlikle temiz bir çözüm, ama benim için Interfacetemelli bir kodun niyetinin daha iyi bir açıklaması gibi geliyor. Ne düşündüğünü bilmek ister misin? - Hbcdev
Cevapladım + 1'ledim ve bunun iyi bir çözüm olduğunu düşünüyorum. Sık kullandığım türden bir desen. Bununla birlikte, her mesaj tipi için bir komisyoncu ile sınırlıdır ve her dinleyici sadece bir tip nesne alabilir. Jenerik, birden fazla türü dinleyebilen herhangi bir nesne türünü ve dinleyiciyi işleyebilen bir aracıya sahip değildir. - Peter Lawrey
Bunu düşünmemiştin, haklısın. Cevabınız için teşekkürler, benim için çok yararlı, umarım diğer insanlara da. - Hbcdev


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

Umarım bu birisi için yararlı olacaktır.


13
2017-11-13 14:37



Neden yapardın if(!contains) { return; } then add() ne zaman yapabilirdin if (contains) { add(); }. Ayrıca, bazı metinler eklemelisiniz. IllegalArgumentException - Alex
Tahmin et demek istedin if(contains) ilk kez ve if(!contains) ikinci kez yuvarlak. Çünkü stili tercih ederim, hepsi bu. Fark yaratmıyor. Ve evet, cevabı istisna için dışarı çıkarılan metinle düzenleyeceğim. - Hbcdev
Hey @Alex, düşün. ! hala yanlış yerde. :) - Hbcdev
Mükemmel ... patlama! Bence, <T> yerine <T> yerine <T> seçeneğini kullanarak benzersizliği ele alalım. - Mehraj Malik


Modern bir güncelleme:  ReactiveX Observer modeline dayanan eşzamansız programlama için çok güzel bir API ve tamamen jenerik. Kodunuzdaki bir yerden diğerine "akış" veri veya olayları gözlemlemek için Gözlemci / Gözlemsel'i kullanıyorsanız, kesinlikle ona bakmalısınız.

İşlevsel programlamaya dayanıyor, dolayısıyla Java 8'in lambda sözdizimi ile çok şık görünüyor:

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

4
2017-07-14 12:34



ReactiveX için +1, ayrıca Android geliştirmek için de kullanılabilir;) (örneğin github.com/ReactiveX/RxAndroid) - optional


Bir keresinde Java için gözlemci modelinin genel bir uygulamasını yazdım. dinamik proxy'ler. İşte nasıl kullanılabileceğine dair bir örnek:

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'ın tam kaynak kodu pastebin'de mevcuttur. Geri dönüş yolu nasıl biraz daha ayrıntılı çalıştığı hakkında blog yazdıAyrıca ilgili projelere de atıfta bulunur.

Jaana, farklı yaklaşımların ilginç bir özetini yazdıAyrıca dinamik proxy yaklaşımını diğerleriyle karşılaştırır. Tabii ki çok teşekkürler Lalonde'dan orijinal fikre sahip olduğum. Hala kontrol etmedim PerfectJPatternama sadece içerebilir gözlemci modelinin kararlı bir şekilde uygulanması; en azından olgun bir kütüphane gibi görünüyor.


2
2017-07-25 22:58





Guava sınıf EventBus kullanmayı deneyin.

Bir gözlemciyi şöyle ilan edebilirsin:

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

Yeni bir EventBus şöyle:

EventBus eventBus = new EventBus();

Ve Observer'ı şu şekilde kaydedin:

eventBus.register(new EventObserver());

Son Gözlemci şöyle bildir:

eventBus.post(message);

2
2018-03-27 08:54





Benzer bir istek buldum ama codereview üzerinde yapıldı. Sanırım burada bahsetmeye değer.

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 öğesinden özgün yanıt


0
2018-01-25 23:38