Wie vermeide ich Singleton-Muster für Event Scheduler?


13

Ich möchte einen Event-Scheduler für mein Spiel erstellen. Grundsätzlich möchte ich das Auslösen eines Game-Events planen können. Dies kann ein einmaliger oder ein periodischer Trigger sein (Triggerereignis "E_BIG_EXPLOSION" auf 5 Sekunden Basis ...).

Es ist verlockend zu glauben, dass dies ein guter Ort ist, um einen Singleton zu verwenden, aber Singletons können ziemlich böse sein und sie neigen dazu, sich wie eine Krankheit zu verbreiten ... also versuche ich, sie um jeden Preis zu vermeiden.

Welches Design würden Sie vorschlagen, um die Verwendung von Singleton in diesem Fall zu vermeiden?


Sehen Sie diese Antwort auf Alternativen zu Singletons
BlueRaja - Danny Pflughoeft

Antworten:


12

Sie sollten über einen sehr genau definierten Satz von Schnittstellen verfügen, die zum Senden oder Empfangen von Nachrichten berechtigt sind. Die Angabe eines Verweises auf einen EventScheduler sollte trivial sein. Wenn dies nicht der Fall ist oder Sie der Meinung sind, dass der Event Scheduler an "zu viele" verschiedene Typen übergeben werden muss, liegt möglicherweise ein größeres Designproblem vor Ihnen (eine promiskuitive Abhängigkeit, die Singletons eher verschlimmern, als lösen) ).

Denken Sie daran, dass Sie in diesem Fall keine neue Abhängigkeit injizieren , obwohl die Technik, den Scheduler an die benötigten Schnittstellen weiterzuleiten, eine Form der " Abhängigkeitsinjektion " ist. Dies ist eine Abhängigkeit, die Sie bereits im System haben, aber Sie machen es jetzt zu einer expliziten (im Vergleich zu der impliziten Abhängigkeit eines Singletons). Als Faustregel gilt, dass explizite Abhängigkeiten vorzuziehen sind, da sie sich selbst besser dokumentieren.

Sie können sich auch mehr Flexibilität verschaffen, indem Sie die Benutzer der Ereignisplanung voneinander entkoppeln (da sie nicht unbedingt alle an denselben Planer gebunden sind). Dies kann nützlich sein, um lokale Client / Server-Setups oder eine Reihe anderer Optionen zu testen oder zu simulieren - Möglicherweise benötigen Sie diese anderen Optionen nicht, aber Sie haben keine Anstrengungen unternommen, um sich künstlich von ihnen abzuhalten, was von Vorteil ist.

BEARBEITEN: Alles, was ich meine, wenn ich über das Weitergeben des Schedulers spreche, ist Folgendes: Wenn Sie eine Spielkomponente haben, die für die Reaktion auf Kollisionen verantwortlich ist, wird sie wahrscheinlich über eine Collision-Responder-Factory erstellt, die Teil Ihrer Physik-Ebene ist. Wenn Sie die Factory mit einer Scheduler-Instanz erstellen, kann sie diese Instanz an alle von ihr erstellten Responder übergeben, die sie dann zum Auslösen von Ereignissen (oder zum Abonnieren anderer Ereignisse) verwenden können.

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

Dies ist offensichtlich ein furchtbar konstruiertes und vereinfachtes Beispiel, da ich nicht weiß, was Ihr Spielobjektmodell ist. Es veranschaulicht jedoch, wie die Abhängigkeit vom Ereignis - Scheduler explizit dargestellt wird, und weist ein gewisses Potenzial für eine weitere Kapselung auf (Sie müssten die Responder nicht unbedingt an den Scheduler weiterleiten, wenn sie auf derselben konzeptionellen Ebene wie der an ein übergeordnetes Kollisionsreaktionssystem kommuniziert haben Dies würde jede einzelne Responder-Implementierung von den Implementierungsdetails des Ereignis-Dispatch-Systems isolieren, beispielsweise welches spezifische Ereignis bei einer Kollision ausgelöst werden soll, was für Ihr System ideal sein könnte. - oder nicht).


Vielen Dank für Ihre Antwort, ich dachte über Abhängigkeitsinjektion nach. Es wäre sehr schön, wenn Sie Ihre Lösung etwas besser spezifizieren könnten? (Pseudo-Code? Diagramm?). Ich glaube, ich folge Ihrer Idee, aber vielleicht ist es nur meine eigene Interpretation des Konzepts.
Mr.Gando

Es ist schwierig, dies auf eine Weise zu tun, die nützlich ist, ohne zu wissen, welche Klassen von Ihnen Ereignisse an den Scheduler senden / empfangen.

In meiner Engine kann jedem Game Entity eine "Event Dispatcher" -Komponente
hinzugefügt werden

7

Ein Event-Dispatcher ist einer jener Fälle, in denen ein Singleton nicht die schlechteste Idee der Welt ist, aber ich begrüße Sie, dass Sie versuchen, dies zu vermeiden. Sie könnten einige Ideen finden hier .


1
Danke !, jedes Mal, wenn ein Singleton erstellt wird, stirbt ein Kätzchen;)
Mr.Gando

3

Ich vermeide auch Singletons, aber es gibt einige Objekte, die am sinnvollsten sind, da Singletons und ein zentrales Messaging-System eines davon sind. Trotz der Beschimpfungen, die ich gehört habe, sind Singletons mit Sicherheit viel besser als globale Variablen / Funktionen, da Sie immer bewusst darauf eingehen müssen (im Vergleich zu globalen Werten, die einfach magisch aus dem Nichts auftauchen).

Am Ende jede Nachricht Sender und Empfänger muss über einen gemeinsamen Schnittpunkt, und es ist viel besser Ihre Objekte mit einem gemeinsamen Singleton geteilt durch alles entkoppelt zu halten , anstatt hat jede Ihres Nachrichtenabsender über die Meldeempfänger direkt kennen.

Ich bin mir zwar sicher, dass eine andere Architektur für Ihr Event-System entwickelt werden könnte, aber es scheint mir eine Verschwendung von Aufwand zu sein, diese zu überdenken, insbesondere wenn die Verwendung eines Event-Systems bereits ein großer Gewinn ist, wenn Sie keines verwenden.

BEARBEITEN: Was Ihr spezielles Beispiel für ein Explosionsereignis betrifft, das bei einem periodischen Auslöser ausgelöst wird, würde ich die Ereignisse wahrscheinlich in einem anderen Objekt (wie der Revolverkanone oder was auch immer, das diese Explosionen verursacht) und nicht im zentralen Ereignissystem auslösen lassen. Diese Ereignisse würden jedoch weiterhin an das zentrale Ereignissystem weitergeleitet.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.