J'entends des opinions contradictoires telles que:
- "Les classes Dedicated Manager ne sont presque jamais le bon outil d'ingénierie"
- "Les classes Dedicated Manager sont (actuellement) le meilleur moyen de survivre à un grand projet avec des milliers de ressources"
Prenons une classe ResourceManager classique qui possède les fonctionnalités suivantes:
- Charge des ressources (textures, audio, modèles 3D, etc.)
- Garantit que les actifs ne sont chargés qu'une seule fois en conservant un cache
- La référence compte les actifs pour déterminer s'ils peuvent être désalloués
- Masque la provenance des actifs réels (par exemple, peut être un fichier par actif, ou tous les actifs dans un fichier de package, ou des actifs peuvent même être chargés sur le réseau)
- Peut recharger des ressources sans redémarrer le programme, ce qui est extrêmement utile pour les artistes travaillant sur le jeu.
Supprimons également l'argument "les singletons sont mauvais" de la table en prétendant que ces objets ResourceManager ne sont pas des singletons et sont plutôt transmis via l' injection de dépendances .
Ensuite, il y a l'argument «utiliser une usine» ou «l'appeler une usine». Mon problème avec cela est que, oui, c'est une usine, mais c'est aussi un cache et un rechargeur (faute d'un meilleur mot). L'appeler une usine ne le décrit pas correctement, et si j'en fais une usine correcte, alors où la mise en cache et le rechargement sont-ils implémentés?
Je suis d'accord que les classes "Manager" sont souvent le symptôme d'une mauvaise architecture, mais dans ce cas particulier comment pourrait-elle être restructurée et conserver toutes les fonctionnalités ? Est-ce une situation où une classe "Manager" est réellement appropriée?
la source
Réponses:
Je pense que le problème n'est pas que ce soit une mauvaise conception. Le problème est que le nom "Manager" est indescriptible, comme "Update" et "Process" et "Actor". Il est inutile pour quelqu'un qui tente de comprendre ce système, et cela crée de la confusion si vous avez déjà d'autres classes qui effectuent des opérations sur les actifs.
Cependant, nommer les choses d'une manière qui transmet efficacement leur objectif est vraiment très difficile . La plupart des systèmes sont trop compliqués pour être mis en évidence par leur nom. Le mieux que vous puissiez faire est d'essayer de préciser quelle idée spécifique rend cette classe cohérente, et ce que les autres classes doivent être évidemment différentes, et choisissez un mot unique qui correspond à ce type.
À mon humble avis, la caractéristique la plus importante d'un bon nom est qu'il est unique dans le contexte où les gens le verront (la base de code), et il est facile de s'en souvenir. Le test doit être que vous pouvez avoir une conversation avec vos coéquipiers qui connaissent le code sans avoir à clarifier ce à quoi vous faites référence. Cela serait également utile si vous deviez écrire une documentation de référence.
Enfin, considérez que si vous ne pouvez pas décrire cette idée cohérente, elle peut ne pas être très cohérente. Par exemple, vous pouvez séparer la mise en cache et le comptage des références dans un AssetCache et conserver le chargement dans un AssetLoader. Mais cela dépend vraiment de la complexité et de l'entrelacement du code.
Mais si votre AssetManager est suffisamment cohérent et que rien d'autre dans votre jeu n'est AssetManagery, alors il pourrait être parfaitement bien nommé.
la source
"Est-ce que je fais ça au même endroit, ou est-ce que je le fais plusieurs?"
La logique de chargement est généralement centralisée sur un seul point d'accès car elle a tendance à être exécutée uniquement aux points clés de l'application, où nous essayons de la sortir du chemin dès que possible. Cela se produit généralement au démarrage de l'application, au démarrage au niveau du jeu ou lorsque la position du joueur dans le monde exige le chargement d'un nouveau morceau pour garder l'expérience transparente. Étant donné que cela se fait comme un processus par lots (que ce soit dans un bloc massif ou dans de nombreux plus petits, cela n'a pas vraiment d'importance), il est logique d'avoir une méthode ou un ensemble de méthodes à appeler, pour lancer ce processus.
Sur la plupart des plates-formes, le chargement est asynchrone, vous n'avez donc d'autre choix que de garder les flux de code de chargement et les autres logiques d'application séparés, une autre raison d'abstraction des fonctions de gestion des ressources dans leur propre classe.
Enfin, quelques points de réflexion:
Le chargement des ressources n'est pas exécuté très souvent par exemple avec. logique de jeu, cela vaut-il la peine d'ajouter de la complexité à vos objets de jeu individuels pour ce faire? Les objets de jeu sont généralement aussi légers que possible et ne sont destinés qu'à ce qui est nécessaire pendant la simulation.
Au cas où un objet responsable de la propre création et la destruction de ce? Cela peut provoquer des maux de tête majeurs lors de la gestion des entités. Si vous vous attendez à ce qu'un objet exécute son propre processus de création (y compris l'obtention de dépendances de ressources), il aura également la responsabilité de se détruire. Cela peut provoquer des pointeurs pendants / nuls, il faut donc vraiment les gérer de l'extérieur. Voir cette réponse décrivant ce problème plus en détail.
PS Mis à part les arguments de type habituels "Les singletons sont terribles" (qui sont terriblement dogmatiques), avez-vous déjà vu un bon argument contre ResourceManagers? "Les classes Dedicated Manager ne sont presque jamais le bon outil d'ingénierie" n'est pas un argument du tout pour ce cas spécifique .
la source