Was ist der Unterschied zwischen XNA Game Services und verherrlichten globalen Variablen?

10

Die Microsoft.Xna.Framework.GameKlasse verfügt über eine Services- Eigenschaft, mit der der Programmierer seinem Spiel einen Service hinzufügen kann, indem er der Add-Methode den Typ der Klasse und eine Instanz der Klasse bereitstellt.

Anstatt ein AudioComponentan alle Klassen und Methoden zu übergeben, die es erfordern, übergeben Sie jetzt einfach Ihre GameInstanz und suchen den Dienst. ( Service Locator )

Da Spiele viele Dienste haben (GraphicsDevice, SceneGraph, AudioComponent, EffectsManager usw.), werden Sie das Spiel nun im Grunde an alles weitergeben.

Warum also nicht einfach diese Instanzen zu einem Singleton machen? Nun, weil Singletons schlecht sind, weil sie einen globalen Zustand haben, verhindern Sie Tests und machen Sie Ihr Design viel spröder. Service Locators werden für viele gleichermaßen als Anti-Pattern angesehen, da Sie anstatt nur die Abhängigkeit an ein Objekt zu übergeben, einen Service Locator (das Spiel) übergeben, der diese Klasse mit dem Rest der Services koppelt.

Warum werden dann Dienste für die XNA- und Spieleentwicklung empfohlen? Liegt es daran, dass Spiele sich von regulären Programmen unterscheiden und stark mit ihren Komponenten verflochten sind und es sehr umständlich wäre, jede Komponente zu bestehen, die für das Funktionieren einer Klasse erforderlich ist? Sind Game Services gerade dann ein notwendiges Übel im Spieldesign? Gibt es Alternativen, die keine langen Parameterlisten und Kopplungen erfordern?

John Pollick
quelle
3
"Service Locators gelten für viele gleichermaßen als Anti-Pattern" - [Zitieren erforderlich]. Offensichtlich ist es schlecht, sie dort zu verwenden, wo Sie sie nicht benötigen - dies gilt für alle Software-Konstrukte -, aber ich habe noch nie gehört, dass Service Locators als Anti-Pattern bezeichnet werden. (Andererseits versuche ich auch, Programmierer zu vermeiden, die die meiste Zeit damit verbringen, die neuesten
@ JoeWreschnig, obwohl ich Ihnen zustimme, habe ich ein bisschen
Roy T.
2
Ja, aber dieser Typ verkauft ein Buch über die Verwendung von DI. offensichtlich will er nicht, dass du etwas Einfaches benutzt, dann wirst du sein Buch nicht kaufen! (In aller Ernsthaftigkeit behandelte Fowlers ursprünglicher DI / SL-Artikel genau dieses Thema - martinfowler.com/articles/… - und der Blog-Rant dieses Typen fügt der Diskussion nichts hinzu.)
Ich wünschte, ich könnte diesen Link, den Sie gerade von Martin Fowler gepostet haben, als Antwort markieren (obwohl es nicht meine Frage ist)
Roy T.

Antworten:

6

Ich glaube nicht, dass alle zustimmen, dass Service Locators schlecht sind. Service Locators bieten eine Möglichkeit, wichtige Funktionen, die jeder benötigt, von seiner tatsächlichen Implementierung zu entkoppeln. Service Locators bieten auch die Möglichkeit, Services zur Laufzeit auszutauschen. Ich denke auch nicht, dass es wahr ist, dass Sie den Service Locator überall herumreichen müssen. Ich denke, Sie können von überall darauf zugreifen. Während eine Spielklasse mit Globals ungefähr tausend Mal pro Frame bestanden werden müsste (was zu einer schlechten Leistung führen würde).

Betrachten Sie das folgende Beispiel, um den Unterschied zwischen dem Service Locator und einer Klasse mit Globals etwas deutlicher zu machen:

Ich mache ein Spiel und verwende ein gameA-Klassenobjekt mit einer globalen Variablen: dem Content Loader. Jetzt möchte ich in meinem nächsten Spiel nicht die gameA-Klasse verwenden, sondern eine neue gameB-Klasse erstellen. Ich muss jetzt wieder die Globals von Spiel A zu Spiel B hinzufügen, was zusätzliche Arbeit ist. Außerdem wird es schwierig, den gewünschten Content Loader-Typ zu konfigurieren. Angenommen, ich habe ein Spiel, das sowohl auf dem PC als auch auf der XBOX ausgeführt wird. Auf dem PC möchte ich einen Content Loader, der auch ein Dialogfeld "Datei öffnen" öffnen kann. Während ich auf der XBOX bin, möchte ich das definitiv nicht. Mit einer Konfigurationsdatei kann ich den Content Loader sogar nach der Hälfte des Spiels problemlos ändern, selbst von einem anderen Ort als der GameA-Klasse, ohne nur die Globals in GameA öffentlich zu machen.

Stellen Sie sich nun vor, ich verwende einen Service Locator. Ich kann es immer weiter benutzen. Und selbst welche Art von Dienstleistungen es gibt, kann immer anders sein. Es ist auch wahr, dass ich mich nicht einmal um ihre Implementierung kümmern werde, solange sie eine gemeinsame Schnittstelle haben. (Obwohl ich das auch in einer gamA-ähnlichen Klasse verwenden könnte).

Wie auch immer, ich denke, Sie werden die Spieldienste ziemlich praktisch finden. Jeder, den ich kenne, benutzt es. Um dir ein bisschen mehr zu helfen. Ich habe eine kleine Hilfsklasse für Spieledienste erstellt, damit Sie die ganze Zeit weniger Casting durchführen können: http://roy-t.nl/index.php/2010/08/25/xna-accessing-contentmanager -und-Grafik-Gerät-überall-jederzeit-der-Gameservice-Container / Ich benutze es ziemlich oft.

Roy T.
quelle
Danke für die Antwort. Fordern Sie in einer Klasse, für die ein Dienst erforderlich wäre, normalerweise bei Bedarf nur die GameServices-Klasse für den Dienst an, oder erstellen Sie eine Mitgliedsvariable in der Klasse und fordern Sie den Dienst nur im Konstruktor an? Ich frage wohl, wie langsam es return (T)Instance.GetService(typeof(T));ist.
John Pollick
Was halten Sie von dieser Implementierung der GameServices-Klasse? Ich habe den unnötigen Singleton entfernt, weil er keine Funktionen hatte, die die Abstraktion noch nicht hatte. Außerdem habe ich das redundante ServiceSuffix am Ende jeder Methode entfernt.
John Pollick
Ich denke auch, dass der Grund dafür, dass die Services- AddServiceMethode einen Typ annimmt, darin besteht, dass Sie die Schnittstelle des Service angeben können. Zum Beispiel Services.AddService(typeof(IGraphicsDeviceManager), graphics);. Beim Abrufen des Dienstes können Sie dann die Schnittstelle als Typ angeben und in die von Ihnen ausgewählte Unterklasse umwandeln.
John Pollick
@ JohnPollick, für die erste Frage. Ich versuche es immer anzufordern (weil es sich hätte ändern können), aber wenn ich wirklich in jedem Frame etwas brauche, erstelle ich eine Mitgliedsvariable. Ich habe nicht viele Leistungsstatistiken, außer dass es für mich nie ein Engpass war. Das zweite, ja, es sieht gut aus :). Und zum dritten. Dies ist in der Tat der Grund für ein Typargument. Sie sollten sich jedoch niemals angewöhnen, abgerufene Werte herunterzuspielen, da dies Ihre Fähigkeit zerstört, Klassen als Service auszutauschen. Wenn Sie keinen IGraphicsDeviceManger, sondern einen MyGDM möchten, erstellen Sie möglicherweise eine neue Schnittstelle.
Roy T.
1
Oh, ich habe dich nicht auch besetzt gesehen. Natürlich kann es auf den Typ schließen, wenn Sie zuerst wirken :).
Roy T.
5

Warum also nicht einfach diese Instanzen zu einem Singleton machen? Nun, weil Singletons schlecht sind, weil sie einen globalen Zustand haben, verhindern Sie Tests und machen Sie Ihr Design viel spröder.

Singletons nehmen im Allgemeinen viele nützliche Eigenschaften und verpacken sie zu einem schrecklichen Hybrid, der Ihnen einige Dinge bietet, die Sie nicht wollen, zusammen mit dem, was Sie wollten. (Waren es "Create-on-Demand" -Eigenschaften? Globaler Geltungsbereich? Durchsetzung mindestens einer Instanz? Verbot mehrerer Instanzen?)

Service Locators werden für viele gleichermaßen als Anti-Pattern angesehen, da Sie anstatt nur die Abhängigkeit an ein Objekt zu übergeben, einen Service Locator (das Spiel) übergeben, der diese Klasse mit dem Rest der Services koppelt.

Sie können die Kopplung nicht ganz vermeiden. Dinge benutzen andere Dinge und ohne das macht Ihr Programm nichts. Die einzige wirkliche Frage ist also, auf welcher Abstraktionsebene sie sich befindet.

Die meisten Texte zur Objektorientierung unterscheiden zwischen Komposition und Aggregation. Es ist wichtig, den konzeptionellen Unterschied zwischen etwas, das Sie besitzen, und etwas, das Sie kennen, zu kennen. Leider haben die meisten modernen Sprachen hier keine klare Unterscheidung, und ein Verweis auf ein Objekt könnte eines dieser beiden Dinge bedeuten. (Ironischerweise macht C ++ einen besseren Job, wenn Sie die verschiedenen intelligenten Zeigertypen verwenden.)

Eine Möglichkeit, wie Sie können Zusammensetzung machen und Aggregation in Ihrem Code klar von Dienstleistungen für aggregierte Objekte verwenden. Dienste sind eine Möglichkeit, Zugriff auf Dinge zu gewähren, die Sie nicht besitzen, die Sie aber verwenden können.

Warum werden dann Dienste für die XNA- und Spieleentwicklung empfohlen?

Ich denke nicht, dass sie in der Spieleentwicklung allgemein empfohlen werden.

Kylotan
quelle