Die Microsoft.Xna.Framework.Game
Klasse 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 AudioComponent
an alle Klassen und Methoden zu übergeben, die es erfordern, übergeben Sie jetzt einfach Ihre Game
Instanz 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?
quelle
Antworten:
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.
quelle
return (T)Instance.GetService(typeof(T));
ist.Service
Suffix am Ende jeder Methode entfernt.AddService
Methode einen Typ annimmt, darin besteht, dass Sie die Schnittstelle des Service angeben können. Zum BeispielServices.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.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?)
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.
Ich denke nicht, dass sie in der Spieleentwicklung allgemein empfohlen werden.
quelle