Come evitare il modello singleton per Event Scheduler?


13

Voglio creare un programmatore di eventi per il mio gioco, in pratica voglio essere in grado di pianificare l'attivazione di un evento di gioco. Questo può essere un trigger di una volta o un trigger periodico (evento di trigger "E_BIG_EXPLOSION" su una base di 5 secondi ...).

È allettante pensare che questo possa essere un buon posto per usare un Singleton, ma i single possono essere abbastanza malvagi e tendono a diffondersi come una malattia ... quindi cerco di evitarli a tutti i costi.

Quale progetto proporresti per evitare l'uso del singleton in questo caso?


Visualizza questa risposta in alternativa ai singoli
BlueRaja - Danny Pflughoeft

Risposte:


12

Dovresti avere un set ben definito di interfacce che possono trasmettere o ricevere messaggi - dare loro un riferimento a un EventScheduler dovrebbe essere banale. Se non lo è, o se ritieni che ciò implicherebbe il passaggio del programmatore di eventi a "troppi" tipi distinti, potresti avere un problema di progettazione più grande tra le mani (una dipendenza promiscua, che i singoli tendono ad esacerbare, non a risolvere ).

Ricorda che mentre la tecnica di passare lo scheduler alle interfacce che ne hanno bisogno è una forma di " iniezione di dipendenza ", in questo caso non stai iniettando una nuova dipendenza. Questa è una dipendenza che hai già nel sistema, ma ora la stai rendendo esplicita (rispetto alla dipendenza implicita di un singleton). Come regola generale, le dipendenze esplicite sono più preferibili in quanto sono più auto-documentate.

Ti permetti anche una maggiore flessibilità disaccoppiando gli utenti della pianificazione degli eventi l'uno dall'altro (poiché non sono necessariamente tutti legati allo stesso programmatore), che può essere utile per testare o simulare le configurazioni client / server locali o una serie di altre opzioni - potresti non aver bisogno di queste altre opzioni, ma non hai speso sforzi per limitarti artificialmente a loro, il che è un vantaggio.

EDIT: Tutto ciò che intendo quando parlo di passare lo schedulatore in giro è questo: se hai qualche componente di gioco che è responsabile della risposta alla collisione, è probabilmente creato tramite una fabbrica di risponditore di collisioni che fa parte del tuo livello fisico. Se costruisci la factory con un'istanza dello scheduler, può quindi passare quell'istanza a tutti i responder che crea, che può quindi farne uso per generare eventi (o forse iscriversi ad altri eventi).

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;
}

Questo è ovviamente un esempio terribilmente inventato e semplificato poiché non so quale sia il tuo modello di oggetto di gioco; tuttavia illustra esplicitamente che rende esplicita la dipendenza dal programma di pianificazione degli eventi e mostra un potenziale di ulteriore incapsulamento (non è necessario passare i risponditori al programma di pianificazione se comunicano attraverso un sistema di risposta alle collisioni di livello superiore allo stesso livello concettuale del una fabbrica che si occupava degli aspetti fondamentali della raccolta di eventi tramite lo scheduler. Ciò isolerebbe l'implementazione di ogni singolo risponditore dai dettagli di implementazione del sistema di invio degli eventi, come ad esempio quale evento specifico sollevare in caso di collisione, che potrebbe essere l'ideale per il tuo sistema - - o no).


Grazie per la tua risposta, ho pensato all'iniezione di dipendenza. Sarebbe molto bello se potessi specificare la tua soluzione un po 'meglio? (Pseudo-codice? Diagramma?). Penso di seguire la tua idea, ma forse è solo la mia interpretazione del concetto.
Mr.Gando,

È difficile farlo in un modo utile senza sapere quali classi inviano / ricevono eventi al programmatore.

Nel mio motore, qualsiasi entità di gioco può avere un componente "Event Dispatcher" collegato ...
Mr.Gando

7

Un dispatcher di eventi è uno di quei casi in cui un singleton non è la peggior idea del mondo, ma ti applaudo per aver cercato di evitarlo. Potresti trovare alcune idee qui .


1
Grazie! Ogni volta che un singleton viene creato un Kitten Dies;)
Mr.Gando

3

Tendo a evitare anche i singoli, ma ci sono alcuni oggetti che hanno più senso come singoli e un sistema di messaggistica centrale è uno di questi. Nonostante i rant che ho sentito, i singoli sono certamente molto meglio delle variabili / funzioni globali perché devi sempre affrontarlo deliberatamente (rispetto ai valori globali che appaiono magicamente dal nulla).

Alla fine ogni mittente e destinatario del messaggio deve avere un punto di intersezione comune, ed è molto meglio mantenere i tuoi oggetti disaccoppiati avendo un singleton comune condiviso da tutto piuttosto che ognuno dei tuoi mittenti del messaggio direttamente a conoscenza dei destinatari del messaggio.

Mentre sono sicuro che potrebbe essere ideata un'altra architettura per il tuo sistema di eventi, per me sembra uno spreco di sforzi per pensarci troppo, specialmente quando usare un sistema di eventi è già una grande vittoria sul non usarne uno.

EDIT: Per quanto riguarda il tuo esempio specifico di un evento di esplosione inviato su un innesco periodico, probabilmente avrei gli eventi inviati in qualche altro oggetto (come la pistola a torretta o qualunque cosa causasse quelle esplosioni) e non nel sistema di eventi centrale. Tuttavia, tali eventi verrebbero comunque inviati al sistema di eventi centrale.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.