Le moteur utilise déjà beaucoup de singletons. Si quelqu'un l'utilise, il devrait en connaître quelques-unes:
La méconnaissance n'est pas la raison pour laquelle les singletons doivent être évités.
Singleton est nécessaire ou inévitable pour de nombreuses bonnes raisons. Les frameworks de jeu utilisent souvent des singletons car il s'agit d'une conséquence inévitable du fait de n'avoir qu'un seul matériel dynamique. Cela n'a aucun sens de vouloir jamais contrôler ces matériels avec plusieurs instances de leurs gestionnaires respectifs. La surface graphique est un matériel externe dynamique et initialiser accidentellement une seconde copie du sous-système graphique est tout à fait désastreux, car les deux sous-systèmes graphiques se disputeront la question de savoir qui dessine et quand, se écrasant de manière incontrôlable. De même avec le système de file d'attente d'événements, ils se disputeront l'identité des événements souris de manière non déterministe. Lorsque vous traitez avec un matériel externe avec état dans lequel il n'y en a qu'un, singleton est inévitable pour éviter les conflits.
Singleton est également sensible aux gestionnaires de cache. Les caches sont un cas particulier. Ils ne devraient pas vraiment être considérés comme des singleton, même lorsqu'ils utilisent toutes les mêmes techniques que les singletons pour rester en vie et vivre éternellement. Les services de cache sont des services transparents, ils ne sont pas censés modifier le comportement du programme. Par conséquent, si vous remplacez le service de cache par un cache nul, le programme devrait toujours fonctionner, à l'exception du fait qu'il s'exécute plus lentement. Cependant, la principale raison pour laquelle les gestionnaires de cache sont une exception pour singleton est qu’il n’est pas logique de fermer un service de cache avant d’appliquer l’application elle-même, car cela jettera également les objets en cache, ce qui l’évitera de l’avoir comme base de travail. singleton.
Ce sont de bonnes raisons pour lesquelles ces services sont des singletons. Cependant, aucune des bonnes raisons d’avoir des singletons ne s’applique aux cours que vous avez énumérés.
Parce que j'en aurai besoin à des endroits très différents de mon jeu, et un accès partagé serait très pratique.
Ce ne sont pas des raisons pour singletons. Ce sont des raisons pour les globals. C'est également un signe de mauvaise conception si vous devez transmettre beaucoup de choses autour de divers systèmes. Le fait de devoir passer des objets indique un couplage élevé qui peut être empêché par une bonne conception OO.
En regardant la liste des cours de votre article qui, à votre avis, devraient être des singletons, je peux dire que la moitié d’entre eux ne devraient vraiment pas être des singletons et que l’autre moitié semble ne pas même être là au départ. Le fait que vous ayez besoin de passer des objets semble être dû au manque d’encapsulation appropriée plutôt qu’à de bons cas d’utilisation pour des singletons.
Regardons vos cours petit à petit:
PlayerData (score, lives, ...)
LevelData (parameters per levels and level packs)
GameData (you may obtain some data from server to configure the game)
Juste à partir des noms de classes, je dirais que ces classes sentent le modèle Anemic Domain Model . Les objets anémiques génèrent généralement beaucoup de données, ce qui augmente le couplage et alourdit le reste du code. De plus, anemic class cache le fait que vous pensez probablement toujours de manière procédurale plutôt que d'utiliser l'orientation objet pour encapsuler des détails.
IAP (for in purchases)
Ads (for showing ads)
Pourquoi ces cours doivent-ils être des singletons? Il me semble que ces classes devraient être éphémères, elles devraient être évoquées dès qu’elles sont nécessaires et déconstruites lorsque l’utilisateur termine l’achat ou lorsque les annonces ne doivent plus être diffusées.
EntityComponentSystemManager (mananges entity creation and manipulation)
En d'autres termes, un constructeur et une couche de service? Pourquoi cette classe est-elle sans limites claires ni but, même là au départ?
PlayerProgress (passed levels, stars)
Pourquoi la progression du joueur est-elle séparée de la classe de joueur? La classe de joueurs doit savoir comment suivre ses propres progrès. Si vous souhaitez implémenter le suivi des progrès dans une classe différente de celle du joueur pour la séparation des responsabilités, alors PlayerProgress doit être derrière Player.
Box2dManager (manages the physics world)
Je ne peux pas vraiment en dire plus sur celui-ci sans savoir ce que fait réellement ce cours, mais une chose est claire, c'est que ce cours est mal nommé.
Analytics (for collecting some analytics)
SocialConnection (Facebook and Twitter login, share, friend list, ...)
Cela semble être les seules classes où Singleton peut être raisonnable, car les objets de connexion sociale ne sont pas réellement vos objets. Les connexions sociales ne sont que l'ombre d'objets externes qui résident dans un service distant. En d'autres termes, c'est une sorte de cache. Assurez-vous simplement de séparer clairement la partie mise en cache de la partie service du service externe, car cette dernière partie ne devrait pas nécessairement être un singleton.
Une façon d'éviter de transmettre des instances de ces classes consiste à utiliser le transfert de messages. Plutôt que d'appeler directement des instances de ces classes, vous envoyez un message de manière asynchrone au service Analytic et SocialConnection. Ces services sont abonnés pour recevoir ces messages et agir en conséquence. Étant donné que la file d'attente d'événements est déjà un singleton, vous évitez de passer une instance réelle lors de la communication avec un singleton.