with paso gui example ejemplo create java generics observer-pattern

paso - java gui example button



Un patrĂ³n de observador genĂ©rico en Java (6)

El java.util.Observer y java.util.Observable son feos. Requieren el tipo de lanzamientos que hacen que los fanáticos de seguridad de tipo se sientan incómodos, y no se puede definir una clase para ser un Observer de múltiples cosas sin moldes feos. De hecho, en " ¿Cómo sé el objeto genérico que la clase Observer envía en Java? ", Un respondedor dice que solo se debe usar un tipo de datos en cada observador / observable.

Estoy tratando de hacer una versión genérica del patrón de observador en Java para solucionar ambos problemas. No es diferente a la que se mencionó en la publicación mencionada anteriormente, pero esa pregunta no se resolvió obviamente (el último comentario es una pregunta sin respuesta del OP).


Encontré una solicitud similar pero estaba más bien en la vista de código. Creo que vale la pena mencionarlo aquí.

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

Respuesta original de codereview stackexchange


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

Esperemos que esto le sea útil a alguien.


Prefiero usar una anotación para que un oyente pueda escuchar diferentes tipos de eventos.

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

huellas dactilares

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


Trate de usar la clase EventBus of Guava.

Puedes declarar un observador como este:

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

Nuevo un EventBus como este:

EventBus eventBus = new EventBus();

Y registre al observador de esta manera:

eventBus.register(new EventObserver());

Última notificación al observador como:

eventBus.post(message);


Una vez escribí una implementación genérica del patrón de observador para Java usando proxies dinámicos . Aquí hay una muestra de cómo se podría usar:

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

El código fuente completo de AbstractObservable está disponible en pastebin . Hace un tiempo realicé un blog sobre cómo funciona con un poco más de detalle , también me refiero a proyectos relacionados.

Jaana escribió un resumen interesante de diferentes enfoques , que también contrastó el enfoque de proxy dinámico con otros. Muchísimas gracias, por supuesto, a Allain Lalonde, de donde tuve la idea original . Todavía no he verificado PerfectJPattern , pero podría contener una implementación estable del patrón de observador ; Al menos parece una biblioteca madura.


Una actualización moderna: ReactiveX es una API muy agradable para la programación asíncrona basada en el patrón Observer, y es totalmente genérica. Si está utilizando Observer / Observable para "transmitir" datos o eventos de un lugar en su código a otro, definitivamente debería verlo.

Se basa en la programación funcional, por lo que se ve muy elegante con la sintaxis 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);