¿Cómo evitar el patrón singleton para Event Scheduler?

13

Quiero hacer un programador de eventos para mi juego, básicamente quiero poder programar la activación de un evento de juego. Puede ser un desencadenante único o un desencadenante periódico (evento desencadenante "E_BIG_EXPLOSION" en una base de 5 segundos ...).

Es tentador pensar que este puede ser un buen lugar para usar un Singleton, pero los singletons pueden ser bastante malos y tienden a propagarse como una enfermedad ... así que trato de evitarlos a toda costa.

¿Qué diseño propondrías para evitar el uso de singleton en este caso?

Mr.Gando
fuente
Ver esta respuesta en alternativas a singletons
BlueRaja - Danny Pflughoeft

Respuestas:

12

Debe tener un conjunto de interfaces muy bien definido que pueda transmitir o recibir mensajes; darles una referencia a EventScheduler debería ser trivial. Si no lo es, o si cree que eso implicaría pasar el planificador de eventos a "demasiados" tipos distintos, entonces podría tener un problema de diseño más grande en sus manos (una dependencia promiscua, que los puntos únicos tienden a exacerbar, no resolver )

Recuerde que si bien la técnica de pasar el programador a las interfaces que lo necesitan es una forma de " inyección de dependencia ", en este caso no está inyectando una nueva dependencia. Esta es una dependencia que ya tiene en el sistema, pero ahora la está haciendo explícita (frente a la dependencia implícita de un singleton). Como regla general, las dependencias explícitas son más preferibles ya que se documentan mejor.

También se brinda más flexibilidad al desacoplar entre sí los consumidores de la programación de eventos (ya que no todos están necesariamente vinculados al mismo programador), lo que puede ser útil para probar o simular configuraciones locales de cliente / servidor o una serie de otras opciones - Es posible que no necesite estas otras opciones, pero no ha realizado ningún esfuerzo para restringirse artificialmente de ellas, lo cual es una ventaja.

EDITAR: todo lo que quiero decir cuando hablo de pasar el programador es esto: si tienes algún componente del juego que es responsable de responder a la colisión, probablemente se crea a través de una fábrica de respuesta a colisiones que es parte de tu capa de física. Si construye la fábrica con una instancia de planificador, puede pasar esa instancia a cualquier respondedor que cree, que luego puede usarla para generar eventos (o tal vez suscribirse a otros eventos).

class CollisionResponderFactory {
  public CollisionResponderFactory (EventScheduler scheduler) {
     this.scheduler = scheduler;
  }

  CollisionResponder CreateResponder() {
    return new CollisionResponder(scheduler);
  }

  EventScheduler scheduler;
}

class CollisionResponder {
  public CollisionResponder (EventScheduler scheduler) {
    this.scheduler = scheduler;
  }

  public void OnCollision(GameObject a, GameObject b) {
    if(a.IsBullet) {
      scheduler.RaiseEvent(E_BIG_EXPLOSION);
    }
  }

  EventScheduler scheduler;
}

Obviamente, este es un ejemplo terriblemente artificial y simplificado, ya que no sé cuál es su modelo de objeto de juego; sin embargo, ilustra hacer explícita la dependencia en el planificador de eventos y muestra cierto potencial para una mayor encapsulación (no necesariamente tendría que pasar a los respondedores el planificador si se comunicaban a un sistema de respuesta de colisión de nivel superior en el mismo nivel conceptual que el fábrica que se ocupó de los aspectos básicos de la generación de eventos a través del programador. Esto aislaría cada implementación de respondedor individual de los detalles de implementación del sistema de envío de eventos, como qué evento específico generar en caso de colisión, que puede ser ideal para su sistema: - o no).


fuente
Gracias por su respuesta, pensé en la inyección de dependencia. Sería muy bueno si pudiera especificar su solución un poco mejor? (¿Pseudocódigo? ¿Diagrama?). Creo que sigo tu idea, pero tal vez es solo mi propia interpretación del concepto.
Mr.Gando
Es difícil hacerlo de una manera útil sin saber qué clases tuyas envían / ​​reciben eventos al programador.
En mi motor, cualquier entidad de juego puede tener un componente "Despachador de eventos" conectado ...
Mr.Gando
7

Un despachador de eventos es uno de esos casos en los que un singleton no es la peor idea del mundo, pero lo aplaudo por tratar de evitarlo. Puede encontrar algunas ideas aquí .

munificente
fuente
1
¡Gracias !, cada vez que se crea un singleton muere un gatito;)
Mr.Gando
3

También tiendo a evitar los singletons, pero hay algunos objetos que tienen más sentido como singletons y un sistema de mensajería central es uno de ellos. A pesar de los comentarios que he escuchado, los singletons son ciertamente mucho mejores que las variables / funciones globales porque siempre tienes que abordarlo deliberadamente (en comparación con los valores globales que aparecen mágicamente de la nada).

Al final, cada remitente y receptor de mensajes debe tener un punto de intersección común, y es mucho mejor mantener sus objetos desacoplados teniendo un singleton común compartido por todo, en lugar de que cada uno de sus remitentes de mensajes sepa directamente sobre los receptores de mensajes.

Si bien estoy seguro de que podría diseñarse alguna otra arquitectura para su sistema de eventos, para mí parece una pérdida de esfuerzo pensar demasiado, especialmente cuando usar un sistema de eventos ya es una gran victoria sobre no usar uno.

EDITAR: en cuanto a su ejemplo específico de un evento de explosión enviado en un gatillo periódico, probablemente enviaría los eventos en algún otro objeto (como el arma de la torreta o lo que sea que cause esas explosiones) y no en el sistema de eventos central. Sin embargo, esos eventos aún se enviarían al sistema central de eventos.

jhocking
fuente