java - para - manual de programacion android pdf
Marcos de notificación de eventos genéricos y basados en anotaciones (8)
Si bien los marcos de notificación de eventos de Java basados en interfaz han existido desde tiempos precámbricos (por ejemplo, java.beans.PropertyChangeSupport), cada vez es más popular que los marcos usen la notificación de eventos dirigida por anotaciones.
Para un ejemplo, vea JBossCache 2.2 . La clase de oyente tiene sus métodos de escucha anotados, en lugar de ajustarse a una interfaz rígida. Esto es más fácil de programar y más fácil de leer, ya que no tiene que escribir implementaciones vacías de devoluciones de llamada de escucha que no le interesan (y sí, sé sobre superclases de adaptadores de escucha).
Aquí hay una muestra de los documentos de JBossCache:
@CacheListener
public class MyListener {
@CacheStarted
@CacheStopped
public void cacheStartStopEvent(Event e) {
switch (e.getType()) {
case Event.Type.CACHE_STARTED:
System.out.println("Cache has started");
break;
case Event.Type.CACHE_STOPPED:
System.out.println("Cache has stopped");
break;
}
}
@NodeCreated
@NodeRemoved
@NodeVisited
@NodeModified
@NodeMoved
public void logNodeEvent(NodeEvent ne) {
log("An event on node " + ne.getFqn() + " has occured");
}
}
El problema con esto, es que es mucho más un proceso involucrado escribir el marco para apoyar este tipo de cosas, debido a la naturaleza de anotación-reflexión de la misma.
Entonces, antes de comenzar a escribir un marco genérico, esperaba que alguien ya lo hubiera hecho. ¿Alguien ha encontrado tal cosa?
Aquí hay un proyecto similar llamado SJES .
public class SomeController {
private Calculator c1 = new Calculator();
private Calculator c2 = new Calculator();
public SomeController() {
c1.registerReceiver(this);
c2.registerReceiver(this);
c1.add(10, 10);
c2.add(20, 20);
}
@EventReceiver(handleFor="c1")
public void onResultC1(Calculator.Event e) {
System.out.println("Calculator 1 got: " + e.result);
}
@EventReceiver(handleFor="c2")
public void onResultC2(Calculator.Event e) {
System.out.println("Calculator 2 got: " + e.result);
}
@EventReceiver
public void onResultAll(Calculator.Event e) {
System.out.println("Calculator got: " + e.result);
}
}
public class Calculator {
private EventHelper eventHelper = new EventHelper(this);
public class Event {
long result;
public Event(long result) {
this.result = result;
}
}
public class AddEvent extends Event {
public AddEvent(long result) {
super(result);
}
}
public class SubEvent extends Event {
public SubEvent(long result) {
super(result);
}
}
public void unregisterReceiver(Object o) {
eventHelper.unregisterReceiver(o);
}
public void registerReceiver(Object o) {
eventHelper.registerReceiver(o);
}
public void add(long a, long b) {
eventHelper.fireEvent(new AddEvent(a + b));
}
public void sub(long a, long b) {
eventHelper.fireEvent(new SubEvent(a - b));
}
public void pass(long a) {
eventHelper.fireEvent(new Event(a));
}
}
Creo que esto es muy fácil de usar.
El principal problema que veo aquí son los parámetros del método, que restringen qué métodos se pueden usar para qué eventos, y no hay ayuda en tiempo de compilación para eso.
Esto es lo que hace que las interfaces me resulten atractivas para las implementaciones de patrones de observadores, como el modelo de eventos de Java. Las herramientas como eclipse pueden autopropiar talones de método para que no pueda obtener las firmas incorrectas. En su ejemplo, es muy fácil usar el tipo de parámetro incorrecto y nunca saberlo hasta que ocurra un evento (que podría ser un caso de error varios meses después)
Una cosa que puedes probar son mis anotaciones y el procesador para implementar observadores e implementaciones de objetos nulos. Supongamos que tiene
package a.b.c;
public interface SomeListener {
void fee();
void fie();
void fo();
void fum();
}
y quería crear una instancia de oyente. Podrías escribir
package x.y.z;
import a.b.c.SomeListener;
import com.javadude.annotation.Bean;
import com.javadude.annotation.NullObject;
@Bean(nullObjectImplementations = {@NullObject(type = SomeListener.class) })
public class Foo extends FooGen implements SomeListener {
@Override
public void fie() {
// whatever code you need here
}
}
Para crear una fuente para estos eventos, puede escribir
package a.b.c;
import com.javadude.annotation.Bean;
import com.javadude.annotation.Observer;
@Bean(observers = {@Observer(type = SomeListener.class)})
public class Source extends SourceGen {
// SourceGen will have add/remove listener and fire methods
// for each method in SomeListener
}
Consulte http://code.google.com/p/javadude/wiki/Annotations si le interesa. Podría darte algunas otras ideas también.
He creado http://neoevents.googlecode.com para manejar este tipo de controlador de eventos basado en anotaciones.
@actionPerformed
private void onClick() {
//do something
}
protected void initComponents() {
JButton button = new JButton("Click me!!!");
button.addActionListener(new ActionListener(this) );
}
Parece tan simple como esperaba que fuera. Las anotaciones están disponibles para cada oyente en J2SE.
No confundas complicado por inteligente. Me parece que esto sería:
- Una pesadilla para depurar
- Difícil de seguir (desde una perspectiva de mantenimiento, o alguien que intenta cambiar algo 6 meses después)
- Lleno de
if (event instanceof NodeCreatedEvent)
como código. ¿Por qué esto es mejor que crear una subclasificación de unadapter
No tengo ni idea.
También he estado pensando en un marco de eventos impulsado por anotaciones genéricas. Me gustan los beneficios proporcionados por el tipado estático, pero el modelo de evento actual basado en interfaz es doloroso de usar (código feo). ¿Sería posible usar un procesador de anotación personalizado para hacer algunas comprobaciones en tiempo de compilación? Eso podría ayudar a agregar algo de la "seguridad" faltante a la que todos nos hemos acostumbrado.
Una gran parte de la comprobación de errores también se puede realizar en el momento en que los oyentes están "registrados" con los productores del evento. Por lo tanto, la aplicación fallará temprano (cuando los oyentes estén registrados), posiblemente incluso en el momento del inicio.
Aquí hay un ejemplo de cómo podría ser el marco genérico con el que he estado jugando:
public class ExampleProducer {
private EventSupport<ActionEvent> eventSupport;
public ExampleProducer() {
eventSupport = new EventSupport<ActionEvent>(this);
}
@AddListenersFor(ActionEvent.class)
public void addActionListener(Object listener)
{
eventSupport.addListener(listener);
}
@RemoveListenersFor(ActionEvent.class)
public void removeActionListener(Object listener)
{
eventSupport.removeListener(listener);
}
public void buttonClicked() {
eventSupport.fire(new ActionEvent(this,
ActionEvent.ACTION_PERFORMED, "Click"));
}
}
El productor usa EventSupport
, que usa la reflexión para invocar los eventos. Como se mencionó anteriormente, EventSupport
podría realizar algunas verificaciones iniciales cuando se registran los oyentes de eventos.
public class ExampleListener
{
private ExampleProducer submitButton;
public ExampleListener()
{
submitButton = new ExampleProducer();
EventSupport.autoRegisterEvents(this);
}
@HandlesEventFor("submitButton")
public void handleSubmitButtonClick(ActionEvent event)
{
//...some code to handle the event here
}
}
Aquí, EventSupport
tiene un método estático que usa la reflexión para registrar automáticamente el oyente con el productor del evento. Esto elimina la necesidad de registrarse manualmente con el origen del evento. Se podría usar un procesador de anotación personalizado para validar que la anotación @HandlesEventFor
refiera a un campo real del ExampleListener
. El procesador de anotaciones también podría hacer otras comprobaciones, como asegurar que la firma del método del controlador de eventos coincida con uno de los métodos de registro en el ExampleProducer
(básicamente, la misma verificación que se podría realizar en el momento del registro).
¿Qué piensas? ¿Vale la pena invertir algo de tiempo en desarrollarse por completo?
También puede consultar MBassador. Está basado en anotaciones, es muy liviano y utiliza referencias débiles (por lo tanto, fácil de integrar en entornos donde la gestión del ciclo de vida de los objetos se realiza mediante un marco como Spring o Guice o algo así).
Proporciona un mecanismo de filtrado de objetos (por lo tanto, puede suscribirse a NodeEvent y adjuntar algunos filtros para restringir el manejo de mensajes a un conjunto de tipos específicos solamente). También puede definir sus propias anotaciones para tener una declaración personalizada de sus manejadores.
Y es muy rápido y eficiente en el uso de los recursos. Vea este benchmark que muestra un gráfico de rendimiento para diferentes escenarios usando Guava o mbassador.
Ya puedes hacer esto hoy con EventBus .
El siguiente ejemplo es de la guía de Getting Started de EventBus . Barra de estado que se actualiza en función de los eventos publicados y no es necesario registrar el control / widget de la barra de estado como elemento de escucha de los editores. Sin EventBus, la barra de estado deberá agregarse como escucha a muchas clases. La barra de estado también se puede crear y destruir en cualquier momento.
public StatusBar extends JLabel {
public StatusBar() {
AnnotationProcessor.process(this);
}
@EventSubscriber(eventClass=StatusEvent.class)
public void updateStatus(StatusEvent statusEvent) {
this.setText(statusEvent.getStatusText();
}
}
Un proyecto similar es ELF (Event Listener Framework) pero parece ser menos maduro.
Actualmente estoy investigando acerca de los marcos de notificación de eventos en la programación impulsada por evento Publish-Subscribe | Kev''s Spring vs Java EE Dev y los artículos de seguimiento.