pattern observer java design-patterns listeners

observer - Java. Patrón correcto para implementar oyentes



observer java (5)

Creo que lo estás haciendo correctamente, ya que tus interfaces tienen un valor semántico y expresan lo que están escuchando (por ejemplo, gruñidos y maullidos en lugar de pisotones). Con un enfoque genérico, puede reutilizar el código de transmisión, pero puede perder la legibilidad.

Por ejemplo, está java.beans.PropertyChangeSupport que es una utilidad para implementar Oberservers que escucha los cambios de valor. Realiza la difusión, pero aún necesita implementar el método en su clase de dominio y delegar en el objeto PropertyChangeSupport. Los métodos de devolución de llamada no tienen significado por sí mismos, y los eventos transmitidos se basan en cadenas:

public interface PropertyChangeListener extends java.util.EventListener { void propertyChange(PropertyChangeEvent evt); }

Otro es java.util.Observable que proporciona el mecanismo de transmisión, pero tampoco es lo mejor.

Me gusta ElephantListener.onStomp()

Muy típicamente, tengo una situación en la que un objeto dado tendrá que tener muchos oyentes. Por ejemplo, podría tener

class Elephant { public void addListener( ElephantListener listener ) { ... } }

Pero tendré muchas situaciones así. Es decir, también tendré un objeto Tiger que tendrá TigerListener s. Ahora, TigerListener s y ElephantListener s son bastante diferentes:

interface TigerListener { void listenForGrowl( Growl qrowl ); void listenForMeow( Meow meow ); }

mientras

interface ElephantListener { void listenForStomp( String location, double intensity ); }

Encuentro que siempre tengo que seguir implementando nuevamente el mecanismo de transmisión en cada clase de animal, y la implementación es siempre la misma. ¿Hay un patrón preferido?


En lugar de que cada Listener tenga métodos específicos para cada tipo de evento que pueda enviar, cambie la interfaz para aceptar una clase de Event genérica. Luego puede subclasificar Event a subtipos específicos si lo necesita, o hacer que contenga un estado como double intensity .

TigerListener y ElephentListener se convierten en

interface TigerListener { void listen(Event event); }

De hecho, puede refactorizar aún más esta interfaz en un Listener simple:

interface Listener { void listen(Event event); }

Las implementaciones de Listener pueden contener la lógica que necesitan para los eventos específicos que les interesan.

class TigerListener implements Listener { @Overrides void listen(Event event) { if (event instanceof GrowlEvent) { //handle growl... } else if (event instance of MeowEvent) { //handle meow } //we don''t care about any other types of Events } } class ElephentListener { @Overrides void listen(Event event) { if (event instanceof StompEvent) { StompEvent stomp = (StompEvent) event; if ("north".equals(stomp.getLocation()) && stomp.getDistance() > 10) { ... } } } }

La relación clave entre el suscriptor y el editor es que el editor puede enviar eventos a los suscriptores, no es necesariamente que pueda enviarle ciertos tipos de eventos: este tipo de refactorización empuja esa lógica desde la interfaz hacia las implementaciones específicas .


Esta es una respuesta más general para las personas que vienen aquí solo con ganas de hacer un oyente. Estoy resumiendo la creación de escuchas personalizados de CodePath. Lea ese artículo si necesita más explicación.

Aquí están los pasos.

1. Definir una interfaz

Esto está en la clase secundaria que necesita comunicarse con algún padre desconocido.

public class MyClass { // interface public interface MyClassListener { // add whatever methods you need here public void onSomeEvent(String title); } }

2. Crea un Setter de oyentes

Agregue una variable de miembro de escucha privada y un método de establecimiento público a la clase secundaria.

public class MyClass { // add a private listener variable private MyClassListener mListener = null; // provide a way for another class to set the listener public void setMyClassListener(MyClassListener listener) { this.mListener = listener; } // interface from Step 1 public interface MyClassListener { public void onSomeEvent(String title); } }

3. Activar eventos de escucha

El objeto hijo ahora puede llamar a métodos en la interfaz de escucha. Asegúrate de verificar si no hay ningún valor porque es posible que no haya nadie escuchando. (Es decir, es posible que la clase principal no haya llamado al método de establecimiento para nuestro oyente).

public class MyClass { public void someMethod() { // ... // use the listener in your code to fire some event if (mListener != null) mListener.onSomeEvent("hello"); } // items from Steps 1 and 2 private MyClassListener mListener = null; public void setMyClassListener(MyClassListener listener) { this.mListener = listener; } public interface MyClassListener { public void onSomeEvent(String myString); } }

4. Implementar las devoluciones de llamada del oyente en el padre

El padre ahora puede usar el escucha que configuramos en la clase secundaria.

Ejemplo 1

public class MyParentClass { private void someMethod() { MyClass object = new MyClass(); object.setMyClassListener(new MyClass.MyClassListener() { @Override public void onSomeEvent(String myString) { // handle event } }); } }

Ejemplo 2

public class MyParentClass implements MyClass.MyClassListener { public MyParentClass() { MyClass object = new MyClass(); object.setMyClassListener(this); } @Override public void onSomeEvent(String myString) { // handle event } }


Prueba la biblioteca de java kiss y obtendrás esto más rápido y correctamente.

import static kiss.API.*; class Elephant { void onReceiveStomp(Stomp stomp) { ... } } class Tiger { void onReceiveMeow(Meow meow) { ... } void onReceiveGrowl(Growl growl) { ... } } class TigerMeowGenerator extends Generator<Meow> { // to add listeners, you get: // addListener(Object tiger); // anything with onReceiveMeow(Meow m); // addListener(meow->actions()); // any lambda // to send meow''s to all listeners, use // send(meow) }

El generador es seguro y eficiente en subprocesos (escribir los generadores correctos es la parte más difícil). Es una implementación de las ideas en Java Dev. Diario - Escuchar con habilidad en Java (copia local)


Una opción diferente es el patrón de pizarra . Esto desconecta al editor y al suscriptor entre sí, y ninguno de los dos contendrá ningún código de transmisión. Ambos simplemente usan un mecanismo de mensajería para pub / sub y ninguno tiene conexión directa con el otro.

Este es un modelo común para la mensajería en una plataforma OSGi.