2009-03-30 2 views
2

Ok, ich habe einen Spieleserver in Java/Hibernate/Spring/Quartz. Die Spieluhr tickt mit einem Quarz-Timer, und das funktioniert gut.Benötigen Sie eine elegante Möglichkeit, willkürlichen Code in einem bestimmten Intervall aufzurufen

Allerdings habe ich viele andere Dinge, die in bestimmten, veränderlichen Intervallen (in Spielzeit, nicht in Echtzeit) passieren müssen.

Zum Beispiel alle 24 Stunden Spielzeit (~ 47 Minuten Echtzeit, je nach Server Uhr Multiplikator) eine Reihe von verschiedenen einmal täglichen Spiel Aktionen passieren, wie Nachschub, oder was hast du.

Jetzt ist das aktuelle System ziemlich grob, aber funktioniert - ich habe eine Tabelle in der Datenbank, die im Wesentlichen ein Cron ist - ein String-Schlüssel, die Ausführungszeit des nächsten Ereignisses und dann Stunden, Minuten, Sekunden und Tage bis das nächste danach. Der Zeit-Ticker prüft dies und sendet dann eine Nachricht mit diesem Code (dem Ereignis-String-Schlüssel) an eine Warteschlange, fügt die Tage, Minuten, Sekunden der aktuellen Zeit hinzu und legt diese als nächste Ausführungszeit fest.

Der Nachrichten-Listener ist der grody Teil - er schaltet den Schlüssel ein und trifft eine seiner Methoden.

Jetzt verstehe ich, dass dies gut funktionieren kann, aber es ist wirklich nicht gut mit mir sitzen. Was wäre Ihre Lösung dafür, jedes Stück Code in seiner eigenen kleinen Klasse zu haben? Welches Designmuster deckt das ab? (Ich bin mir sicher, dass es einen gibt). Ich habe ein paar Ideen, aber ich würde gerne einige Meinungen hören.

Antwort

0

Ich persönlich würde dies nicht in die Datenbank setzen, sondern einen separaten Dienst im Hintergrund laufen lassen. Dann würde mein Webservice oder meine Webanwendung über Interprozesskommunikation mit diesem Dienst kommunizieren. Ich weiß nicht, wie das in die Java-Welt übersetzt wird.

1

Anstatt eine Reihe von Codes einzuschalten, könnten Sie den Code als Schlüssel für eine Map verwenden, wobei die Werte Objekte sind, die eine Handler-Schnittstelle implementieren. Auf diese Weise können Sie viel flexibler neue Ereignistypen hinzufügen.

Das Muster sieht in etwa wie folgt aus:

private final Map<String, Handler> handlers = new TreeMap<String, Handler>(); 

public void register(String event, Handler handler) { 
    handlers.put(event, handler); 
} 

public void handle(String event) { 
    Handler handler = handler.get(event); 
    if (handler == null) { 
    /* Log or throw an exception for unknown event type. */ 
    } 
    else { 
    handler.execute(); 
    } 
} 

Anstatt explizit Handler registrieren, können Sie so etwas wie Java 6 die ServiceLoader verwenden, um neue Verhaltensweisen fügen Sie einfach von JAR-Dateien in den Klassenpfad fallen.

+0

Ich habe dies nicht als Antwort markiert, weil es die beste Antwort ist - sicher, Mapping ist besser als ein Schalter, aber es ist wirklich nicht elegant. Der ServiceLoader wäre der bessere Ansatz, aber keine der anderen bereits vorhandenen Infrastrukturen wie Spring. –

0

Konzeptionell denke ich, du machst zwei Dinge;

Zuerst haben Sie eine skalierte Version von Zeit. Solange die Beziehung zwischen dieser Zeit und Wandtaktzeit konstant bleibt ich ziemlich sicher bin, würde ich nur dieses Skalierungsverhalten delegieren, auf eine einzige Klasse, die hätte Signaturen wie

DateTime getFutureTime(VirtualTimeSpan timespan) 

ich verwenden würde Dies um virtuelle Zeitspannen auf Instanzen von Echtzeit abzubilden. Danach können Sie in Echtzeit arbeiten, was die Dinge wahrscheinlich ein wenig vereinfacht, da Sie die Standard-Planungsfunktionen verwenden können.

Der zweite Teil betrifft die Planung der Arbeit für einen zukünftigen Arbeitsprozess. Es gibt eine Reihe von Kerntechnologien, die damit arbeiten; Konzeptionell denke ich JMS ist der Java-Grand-Dad von vielen von diesen, es definiert Konzepte, die denen, die Sie verwenden, und was Sie brauchen. Ich denke, JMS ist in Ordnung, wenn Sie Konzepte sehen, die Sie vielleicht interessant finden. Es verwendet Selektoren, um Aufgaben an bestimmte Mitarbeiter zu senden, ähnlich wie Sie es beschreiben.

Leider schien JMS nie die Rechnung für die meisten Leute zu passen. Viele Leute fanden es zu schwergewichtig oder die Implementierungen zu fehlerhaft. In der Regel kamen die Leute mit hausgemachten Warteschlangentechnologien aus. Aber die Konzepte sind alle da. Kannst du nicht einfach Quarz verwenden?

1

Ich würde eine Variante der Command Pattern verwenden. Ich würde das Befehlsmuster erweitern, um eine IIntervalCommand-Klasse zu erstellen. Es hätte eine Intervalleigenschaft und eine schreibgeschützte CanExecute-Eigenschaft zusätzlich zur Execute-Methode.

Dann erstellen Sie eine CommandList-Klasse, die eine Liste von IIntervalCommands enthält. Es hätte eine Methode namens CheckToExecute, die Sie die aktuelle Spielzeit übergeben. Die CheckToExecute-Methode durchläuft die Liste, die CanExecute für jeden Befehl aufruft. CanExecute gibt true zurück, wenn die verstrichene Zeit abgelaufen ist. Wenn CanExecute true zurückgibt, ruft CheckToExecute die Execute-Methode des Objekts auf, das IIntervalCommand implementiert.

Das Hinzufügen zusätzlicher Spielereignisse ist eine Frage der Erstellung einer neuen Klasse, die IIntervalClass implementiert. Instanziieren des Objekts und Hinzufügen zur IntervalCommandList.

Wenn die Verarbeitung des Ereignisses zeitaufwendig ist, kann der Befehl die Verarbeitung als separaten Thread generieren. Es wird false an seine CanExecute-Eigenschaft zurückgeben, bis der Thread zurückgegeben wird, selbst wenn das Intervall erneut überschritten wurde. Oder Sie spawnen einen anderen Thread, wenn das Intervall erneut verstrichen ist.

Sie vermeiden die Giant Case-Anweisung. Sie können die Datenbank eliminieren und die Parameter beim Instanziieren der Objekte einrichten. Oder behalten Sie es und verwenden Sie es als Teil einer Fabrik, die all Ihre IIntervalCommands erstellt.

1

Anstatt den Schlüssel einzuschalten, können Sie eine Hashtabelle verwenden, um diese Ereignisse zu versenden. Auf diese Weise müssen Ihre Timer-Ereignisse nicht voneinander wissen.

Es sollte möglich sein, etwas tun haben wie:

timerQueue.registerHandler("key",new TimerHandler(){ 
    // do something timer related 
}); 

Auf diese Weise können Java-Code Umgang mit Ereignissen ohne Ihre beharrte Warteschlange von Ereignissen zu verlieren neu starten können.

http://en.wikipedia.org/wiki/Priority_queue'>Prioritätswarteschlangen sind einen Blick wert, wenn Sie es nicht schon getan haben.

Verwandte Themen