Der Motor verbraucht bereits viele Singletons. Wenn jemand es benutzt hat, sollte er mit einigen von ihnen vertraut sein:
Unbekanntheit ist nicht der Grund, warum Singletons vermieden werden müssen.
Es gibt viele gute Gründe, warum Singleton notwendig oder unvermeidbar ist. Spiel-Frameworks verwenden häufig Singletons, da dies eine unvermeidliche Folge der Verwendung einer einzigen Stateful-Hardware ist. Es macht keinen Sinn, diese Hardware jemals mit mehreren Instanzen ihrer jeweiligen Handler steuern zu wollen. Die Grafikoberfläche ist eine externe Stateful-Hardware, und das versehentliche Initialisieren einer zweiten Kopie des Grafik-Subsystems ist geradezu katastrophal, da sich die beiden Grafik-Subsysteme jetzt gegenseitig darum streiten, wer wann zeichnen darf und sich unkontrolliert überschreiben. Ähnlich wie beim Ereigniswarteschlangensystem streiten sie sich darum, wer die Mausereignisse auf nicht deterministische Weise abruft. Wenn es sich um eine zustandsbehaftete externe Hardware handelt, in der es nur eine gibt, ist Singleton unvermeidlich, um Konflikte zu vermeiden.
Ein weiterer Ort, an dem Singleton sinnvoll ist, sind Cache-Manager. Caches sind ein Sonderfall. Sie sollten nicht wirklich als Singleton betrachtet werden, selbst wenn sie dieselben Techniken wie Singletons anwenden, um am Leben zu bleiben und für immer zu leben. Caching-Dienste sind transparente Dienste. Sie sollen das Verhalten des Programms nicht ändern. Wenn Sie also den Cache-Dienst durch einen Null-Cache ersetzen, sollte das Programm weiterhin funktionieren, außer dass es nur langsamer ausgeführt wird. Der Hauptgrund, warum Cache-Manager eine Ausnahme von Singleton darstellen, ist, dass es nicht sinnvoll ist, einen Cache-Dienst vor dem Herunterfahren der Anwendung selbst herunterzufahren, da dadurch auch die zwischengespeicherten Objekte verworfen werden, was den Punkt zunichte macht, dass es sich um einen handelt Singleton.
Das sind gute Gründe, warum diese Dienste Singletons sind. Keiner der guten Gründe, Singletons zu haben, trifft jedoch auf die von Ihnen aufgelisteten Klassen zu.
Weil ich sie an sehr unterschiedlichen Orten in meinem Spiel brauche und ein gemeinsamer Zugriff sehr praktisch wäre.
Das sind keine Gründe für Singletons. Das sind Gründe für Globals. Dies ist auch ein Zeichen für schlechtes Design, wenn Sie viele Dinge in verschiedenen Systemen austauschen müssen. Das Weitergeben von Dingen weist auf eine hohe Kopplung hin, die durch ein gutes OO-Design verhindert werden kann.
Wenn Sie sich nur die Liste der Klassen in Ihrem Beitrag ansehen, von denen Sie glauben, dass sie Singletons sein müssen, kann ich sagen, dass die Hälfte davon eigentlich keine Singletons sein sollte und die andere Hälfte den Anschein hat, dass sie gar nicht erst da sein sollten. Dass Sie Objekte weitergeben müssen, liegt eher an der fehlenden Kapselung als an den tatsächlichen guten Anwendungsfällen für Singletons.
Lassen Sie uns Ihre Klassen Stück für Stück betrachten:
PlayerData (score, lives, ...)
LevelData (parameters per levels and level packs)
GameData (you may obtain some data from server to configure the game)
Nur von den Klassennamen würde ich sagen, dass diese Klassen nach anämischem Domänenmodell- Antipattern riechen. Anämische Objekte führen normalerweise dazu, dass viele Daten weitergegeben werden müssen, was die Kopplung erhöht und den Rest des Codes umständlich macht. Außerdem verbirgt die anämische Klasse die Tatsache, dass Sie wahrscheinlich immer noch prozedural denken, anstatt die Objektorientierung zum Einkapseln von Details zu verwenden.
IAP (for in purchases)
Ads (for showing ads)
Warum müssen diese Klassen Singletons sein? Es scheint mir, dass dies kurzlebige Klassen sein sollten, die aufgerufen werden sollten, wenn sie benötigt werden, und dekonstruiert werden sollten, wenn der Benutzer den Kauf abschließt oder wenn die Anzeigen nicht mehr gezeigt werden müssen.
EntityComponentSystemManager (mananges entity creation and manipulation)
Mit anderen Worten, ein Konstruktor und eine Serviceschicht? Warum gibt es in dieser Klasse überhaupt keine klaren Grenzen und keinen klaren Zweck?
PlayerProgress (passed levels, stars)
Warum ist der Spielerfortschritt von der Spielerklasse getrennt? Die Player-Klasse sollte wissen, wie sie ihren eigenen Fortschritt verfolgt. Wenn Sie die Fortschrittsverfolgung in einer anderen Klasse als der Player-Klasse implementieren möchten, um die Verantwortung zu trennen, sollte PlayerProgress hinter Player stehen.
Box2dManager (manages the physics world)
Ich kann nicht weiter darauf eingehen, ohne zu wissen, was diese Klasse tatsächlich tut, aber eins ist klar: Diese Klasse hat einen schlechten Namen.
Analytics (for collecting some analytics)
SocialConnection (Facebook and Twitter login, share, friend list, ...)
Dies scheinen die einzigen Klassen zu sein, in denen Singleton sinnvoll sein kann, da die sozialen Verbindungsobjekte eigentlich nicht Ihre Objekte sind. Die sozialen Verbindungen sind nur ein Schatten von externen Objekten, die in einem entfernten Dienst leben. Mit anderen Worten, dies ist eine Art Cache. Stellen Sie nur sicher, dass Sie den Caching-Teil klar vom Service-Teil des externen Dienstes trennen, da der letztere Teil kein Singleton sein muss.
Eine Möglichkeit, die Weitergabe von Instanzen dieser Klassen zu vermeiden, ist die Verwendung der Nachrichtenübergabe. Anstatt Instanzen dieser Klassen direkt aufzurufen, senden Sie stattdessen asynchron eine an den Analytic- und SocialConnection-Dienst adressierte Nachricht, und diese Dienste sind abonniert, um diese Nachrichten zu empfangen und auf die Nachricht zu reagieren. Da es sich bei der Ereigniswarteschlange bereits um einen Singleton handelt, müssen Sie bei der Kommunikation mit einem Singleton keine tatsächliche Instanz übergeben, indem Sie den Anruf mit einem Trampolin versehen.