Introdução
No desenvolvimento de sistemas e, principalmente de jogos, é comum precisarmos que diferentes sistemas se comuniquem sem criar dependências diretas entre eles. O padrão Event Bus é uma solução para esse problema, permitindo que eventos sejam publicados e consumidos sem que os emissores conheçam diretamente os ouvintes.
Neste artigo, vou apresentar um Event Bus que desenvolvi para o meu framework de desenvolvimento de jogos, explicando sua estrutura, funcionalidades e vantagens.
Arquitetura do Event Bus
O Event Bus que desenvolvi segue uma abordagem genérica e tipada, garantindo flexibilidade e segurança no tratamento de eventos. Ele é composto por três principais componentes:
- Interface IEvent: Define a estrutura base para todos os eventos.
- EventBinding: Responsável por armazenar callbacks para eventos específicos.
- EventBus: Um sistema estático que gerencia o registro, a remoção e a publicação de eventos.
1. Interface IEvent
A interface IEvent serve como um contrato para garantir que todos os eventos sigam uma estrutura comum.
namespace NoxStudios.Core.EventBus
{
public interface IEvent { }
}
Todos os eventos que desejamos publicar no Event Bus devem implementar essa interface.
2. EventBinding: Armazenando Callbacks
O EventBinding<T> gerencia as ações que serão executadas quando um evento do tipo T for disparado.
public class EventBinding<T> : IEventBinding<T> where T : IEvent
{
private Action<T> _onEvent = _ => { };
private Action _onEventNoArgs = () => { };
public EventBinding(Action<T> onEvent) => _onEvent = onEvent;
public EventBinding(Action onEventNoArgs) => _onEventNoArgs = onEventNoArgs;
public void Add(Action<T> onEvent) => _onEvent += onEvent;
public void Remove(Action<T> onEvent) => _onEvent -= onEvent;
public void Add(Action onEvent) => _onEventNoArgs += onEvent;
public void Remove(Action onEvent) => _onEventNoArgs -= onEvent;
}
Com essa classe, podemos vincular múltiplas ações a um evento específico.
3. O Event Bus: Registro, Remoção e Publicação
A classe EventBus<T> gerencia o ciclo de vida dos eventos.
public static class EventBus<T> where T : IEvent
{
private static readonly HashSet<IEventBinding<T>> Bindings = new();
public static void Register(EventBinding<T> binding) => Bindings.Add(binding);
public static void Unregister(EventBinding<T> binding) => Bindings.Remove(binding);
public static void Publish(T tEvent)
{
foreach (var binding in Bindings)
{
binding.OnEvent(tEvent);
binding.OnEventNoArgs();
}
}
}
Aqui, os eventos são armazenados em um HashSet, garantindo que cada binding seja registrado apenas uma vez.
Benefícios do Event Bus
- Desacoplamento: Componentes podem se comunicar sem depender diretamente uns dos outros.
- Flexibilidade: Podemos adicionar e remover ouvintes dinamicamente.
- Escalabilidade: Reduz a complexidade do código conforme o projeto cresce.
- Reutilização: A arquitetura facilita a reutilização de código entre diferentes projetos.
Exemplo de Uso
- Criamos um evento:
public class PlayerDiedEvent : IEvent { }
- Registramos um listener:
EventBus<PlayerDiedEvent>.Register(new EventBinding<PlayerDiedEvent>(() => Debug.Log("O jogador morreu!")));
- Publicamos o evento:
EventBus<PlayerDiedEvent>.Publish(new PlayerDiedEvent());
Conclusão
Esse sistema de Event Bus oferece uma abordagem poderosa para a comunicação entre componentes no seu sistema, reduzindo acoplamento e melhorando a organização do código. Se bem utilizado, pode tornar a manutenção e expansão do seu sistema muito mais eficiente.
