Bus de eventos usando Reactive Extensions

Utilizar un bus de eventos dentro de una aplicación es algo bastante común. Un ejemplo muy sencillo podría ser desacoplar la lógica de envío de notificaciones al usuario en WPF desde los ViewModels sin necesidad de recurrir a la inyección de dependencias en el ViewModel. Aunque hasta ahora había recurrido siempre a programar mi propio bus de eventos, utilizar Reactive Extensions me ha resultado muy interesante, útil y sencillo. Este sería un ejemplo de bus de eventos utilizando RX:

public static class ReactiveEventBus {
    private static readonly Subject<object> messageSubject = new Subject<object>();

    public static void Send<T>(T message) {
        messageSubject.OnNext(message);
    }

    public static IObservable<T> AsObservable<T>() { return messageSubject.OfType<T>(); }
}

Este código lo encontré aquí.

Lo habitual en un bus de eventos es tener un mecanismo para enviar eventos y otro para suscribirnos a ellos. En este caso, en lugar de habilitar la suscripción, lo que me proporciona es la posibilidad de obtener un IObservable, siendo T el tipo del mensaje que quiero observar.

Aplicado al ejemplo de las notificaciones, en la MainWindow (o donde sea) podría añadir este código:

var subscription = ReactiveEventBus.AsObservable<GlobalMessage>().Subscribe(message => MessageBox.Show(message.Message));
Closed += (sender, args) => subscription.Dispose();

Y eso me permitiría poder enviar mensajes desde cualquier ViewModel (o cualquier otro sitio) con este código:

ReactiveEventBus.Send(new GlobalMessage("Hola!"));

Es muy importante tener en cuenta la llamada al Dispose() de la suscripción para evitar problemas de liberación de recursos.

Lo interesante de que el bus de eventos me de un IObservable es que, combinado con RX puedo hacer cosas más complejas de una forma muy sencilla, como:

ReactiveEventBus.AsObservable<ScopedEvent>()
    .Where(evt => evt.Scope == "Scope1")
    .Subscribe(evt => PUT YOUR CODE FOR Scope1 HERE);

ReactiveEventBus.AsObservable<ScopedEvent>()
    .Where(evt => evt.Scope == "Scope2")
    .Subscribe(evt => PUT YOUR CODE FOR Scope2 HERE);

Respecto a si tiene sentido el uso de una propiedad para determinar el scope o sería más adecuado utilizar un objeto de distinto tipo, la respuesta es la de siempre: depende 😉

Algunos casos en los que me parece interesante son el envío un evento destinado únicamente a los UserControl que están mostrando una entidad concreta en un interfaz MDI o hacer que el scope de un evento sea una ventana concreta.

Modesto San Juan

Desarrollo software e intento hacerlo bien