Je suis un développeur de jeux en herbe, je travaille sur des jeux indépendants occasionnels, et depuis un moment, je fais quelque chose qui semblait être une mauvaise pratique au début, mais je veux vraiment obtenir une réponse de certains programmeurs expérimentés ici.
Disons que j'ai un fichier appelé enumList.h
où je déclare toutes les énumérations que je veux utiliser dans mon jeu:
// enumList.h
enum materials_t { WOOD, STONE, ETC };
enum entity_t { PLAYER, MONSTER };
enum map_t { 2D, 3D };
// and so on.
// Tile.h
#include "enumList.h"
#include <vector>
class tile
{
// stuff
};
L'idée principale est que je déclare toutes les énumérations du jeu dans un fichier , puis que j'importe ce fichier lorsque j'ai besoin d'en utiliser une certaine énumération, plutôt que de le déclarer dans le fichier où je dois l'utiliser. Je le fais parce que cela rend les choses propres, je peux accéder à chaque énumération en un seul endroit plutôt que d'avoir des pages ouvertes uniquement pour accéder à une énumération.
Est-ce une mauvaise pratique et peut-elle affecter les performances de quelque manière que ce soit?
la source
materials_t
fichiers qui n'en traitent pas, il devra être reconstruit.enumList.h
servant de collection de#include
s pour ces fichiers. Cela permet aux fichiers qui n'ont besoin que d'une seule énumération de l'obtenir directement, tout en fournissant un package singulier pour tout ce qui les désire vraiment tous.Réponses:
Je pense vraiment que c'est une mauvaise pratique. Lorsque nous mesurons la qualité du code, il y a ce que nous appelons la «granularité». Votre granularité souffre gravement en mettant toutes ces énumérations dans un seul fichier et donc la maintenabilité en souffre également.
J'aurais un seul fichier par énumération pour le trouver rapidement et le regrouper avec le code comportemental de la fonctionnalité spécifique (par exemple, énumération des matériaux dans le dossier où se trouve le comportement des matériaux, etc.);
Vous pourriez penser que c'est propre, mais en fait, ce n'est pas le cas. C'est coupler des choses qui ne vont pas ensemble au niveau des fonctionnalités et des modules et diminue la modularité de votre application. En fonction de la taille de votre base de code et de la façon dont vous souhaitez que votre code soit structuré, cela peut évoluer vers un problème plus important et un code / des dépendances impurs dans d'autres parties de votre système. Cependant, si vous écrivez simplement un petit système monolithique, cela ne s'applique pas nécessairement. Pourtant, je ne le ferais pas comme ça même pour un petit système monolithique.
la source
Oui, c'est une mauvaise pratique, non pas à cause des performances mais à cause de la maintenabilité.
Il rend les choses "propres" uniquement dans le TOC "rassemble des choses similaires". Mais ce n'est en fait pas le type de "propreté" utile et bon.
Les entités de code doivent être regroupées pour maximiser la cohésion et minimiser le couplage , ce qui est mieux réalisé en les regroupant en modules fonctionnels largement indépendants. Les regrouper selon des critères techniques (tels que le regroupement de toutes les énumérations) aboutit à l'inverse - il couple le code qui n'a absolument aucune relation fonctionnelle et place les énumérations qui ne peuvent être utilisées qu'à un seul endroit dans un fichier différent.
la source
Eh bien, ce ne sont que des données (pas un comportement). Le pire qui pourrait / pourrait théoriquement se produire est que le même code est inclus et compilé plus d'une fois produisant un programme relativement plus grand.
Il est tout simplement impossible que ces inclusions puissent ajouter plus de cycles à vos opérations, étant donné qu'il n'y a pas de code comportemental / procédural à l'intérieur (pas de boucles, pas de if, etc.).
Un programme (relativement) plus grand peut difficilement affecter les performances (vitesse d'exécution) et, en tout cas, n'est qu'une préoccupation théorique éloignée. La plupart des compilateurs (peut-être tous) gèrent les inclusions de manière à éviter de tels problèmes.
À mon humble avis, les avantages que vous obtenez avec un seul fichier (code plus lisible et plus gérable) dépassent largement tout inconvénient possible.
la source
Pour moi, tout dépend de la portée de votre projet. S'il y a un fichier avec disons 10 structures et que ce sont les seuls jamais utilisés, je serais parfaitement à l'aise d'avoir un fichier .h pour cela. Si vous avez plusieurs types de fonctionnalités, par exemple des unités, une économie, des bâtiments, etc., je diviserais définitivement les choses. Créez un units.h où résident toutes les structures qui traitent des unités. Si vous voulez faire quelque chose avec des unités quelque part, vous devez inclure units.h bien sûr, mais c'est aussi un "identifiant" sympa que quelque chose avec des unités soit probablement fait dans ce fichier.
Regardez-le de cette façon, vous n'achetez pas de supermarché parce que vous avez besoin d'une canette de coca;)
la source
Je ne suis pas un développeur C ++, donc cette réponse sera plus générique pour OOA & D:
En règle générale, les objets de code doivent être regroupés en termes de pertinence fonctionnelle, pas nécessairement en termes de constructions spécifiques au langage. La question clé oui-non qui devrait toujours être posée est la suivante: "le codeur final devra-t-il utiliser la plupart ou la totalité des objets qu'il obtient lors de la consommation d'une bibliothèque?" Si oui, groupez-vous. Sinon, envisagez de diviser les objets de code et de les placer plus près des autres objets qui en ont besoin (augmentant ainsi les chances que le consommateur ait besoin de tout ce à quoi il accède réellement).
Le concept de base est "haute cohésion"; les membres de code (des méthodes d'une classe jusqu'aux classes dans un espace de noms ou une DLL, et les DLL elles-mêmes) doivent être organisés de telle sorte qu'un codeur puisse inclure tout ce dont ils ont besoin, et rien qu'ils ne le font pas. Cela rend la conception globale plus tolérante au changement; les choses qui doivent changer peuvent être, sans affecter d'autres objets de code qui n'ont pas eu à changer. Dans de nombreuses circonstances, cela rend également l'application plus efficace en mémoire; une DLL est chargée dans son intégralité en mémoire, quelle que soit la quantité de ces instructions exécutées par le processus. La conception d'applications "lean" nécessite donc de prêter attention à la quantité de code qui est tirée en mémoire.
Ce concept s'applique à pratiquement tous les niveaux d'organisation du code, avec des degrés variables d'impact sur la maintenabilité, l'efficacité de la mémoire, les performances, la vitesse de génération, etc. qui ne dépend d'aucun autre objet dans cette DLL, alors l'inclusion de cet objet dans la DLL devrait probablement être repensée. Cependant, il est également possible d'aller trop loin dans l'autre sens; une DLL pour chaque classe est une mauvaise idée, car elle ralentit la vitesse de construction (plus de DLL à reconstruire avec la surcharge associée) et fait du versioning un cauchemar.
Exemple: si une utilisation réelle de votre bibliothèque de codes impliquait l'utilisation de la plupart ou de la totalité des énumérations que vous placez dans ce fichier "enumerations.h" unique, alors par tous les moyens, regroupez-les; vous saurez où chercher pour les trouver. Mais si votre codeur consommateur pouvait théoriquement avoir besoin d'une ou deux des douzaines d'énumérations que vous fournissez dans l'en-tête, alors je vous encouragerais à les mettre dans une bibliothèque distincte et à en faire une dépendance de la plus grande avec le reste des énumérations. . Cela permet à vos codeurs d'obtenir uniquement celui ou les deux qu'ils souhaitent sans se lier à la DLL plus monolithique.
la source
Ce genre de chose devient un problème lorsque plusieurs développeurs travaillent sur la même base de code.
J'ai certainement vu des fichiers mondiaux similaires devenir un lien pour les collisions de fusion et toutes sortes de problèmes sur des projets (plus importants).
Cependant, si vous êtes le seul à travailler sur le projet, vous devez faire ce qui vous convient le mieux et n'adopter des "meilleures pratiques" que si vous comprenez (et êtes d'accord avec) la motivation qui les sous-tend.
Il vaut mieux faire des erreurs et en tirer des leçons que de risquer de rester coincé avec des pratiques de programmation culte du fret pour le reste de votre vie.
la source
Oui, c'est une mauvaise pratique de le faire sur un grand projet. BAISER.
Un jeune collègue a renommé une simple variable dans un fichier .h de base et 100 ingénieurs ont attendu 45 minutes pour que tous leurs fichiers soient reconstruits, ce qui a affecté les performances de chacun. ;)
Tous les projets commencent petit, ballon au fil des ans, et nous maudissons ceux qui ont pris des raccourcis précoces pour créer de la dette technique. Meilleures pratiques, limitez le contenu .h mondial à ce qui est nécessairement mondial.
la source
Dans ce cas, je dirais que la réponse idéale est que cela dépend de la façon dont les énumérations sont consommées, mais que dans la plupart des cas, il est probablement préférable de définir toutes les énumérations séparément, mais si l'une d'entre elles est déjà couplée par conception, vous devez fournir un des moyens pour introduire collectivement lesdits énumérations couplées. En effet, vous avez une tolérance de couplage jusqu'à la quantité de couplage intentionnel déjà présente, mais pas plus.
Compte tenu de cela, la solution la plus flexible est susceptible de définir chaque énumération dans un fichier distinct, mais de fournir des packages couplés lorsque cela est raisonnable (tel que déterminé par l'utilisation prévue des énumérations impliquées).
La définition de toutes vos énumérations dans le même fichier les couple et, par extension, tout code qui dépend d'une ou plusieurs énumérations dépend de toutes les énumérations, que le code utilise réellement d'autres énumérations.
renderMap()
préfère de beaucoup ne savoir que celamap_t
, car sinon tout changement apporté aux autres l'affectera même s'il n'interagit pas réellement avec les autres.Cependant, dans le cas où les composants sont déjà couplés ensemble, fournir plusieurs énumérations dans un seul package peut facilement fournir une clarté et une simplicité supplémentaires, à condition qu'il y ait une raison logique claire pour les énumérations à coupler, que l'utilisation de ces énumérations est également couplée, et que leur fourniture n'introduit pas non plus de couplages supplémentaires.
Dans ce cas, il n'y a pas de lien direct et logique entre le type d'entité et le type de matériau (en supposant que les entités ne sont pas constituées d'un des matériaux définis). Si, cependant, nous avions un cas où, par exemple, une énumération dépend explicitement de l'autre, alors il est logique de fournir un seul package contenant toutes les énumérations couplées (ainsi que tout autre composant couplé), de sorte que le couplage puisse être isolé de ce paquet autant qu'il est raisonnablement possible.
Mais hélas ... même lorsque deux énumérations sont intrinsèquement couplées, même si c'est quelque chose d'aussi fort que "la deuxième énumération fournit des sous-catégories pour la première énumération", il peut arriver un moment où une seule énumération est nécessaire.
Par conséquent, puisque nous savons à la fois qu'il peut toujours y avoir des situations où la définition de plusieurs énumérations dans un seul fichier peut ajouter un couplage inutile, et que la fourniture d'énumérations couplées dans un seul package peut clarifier l'utilisation prévue et nous permettre d'isoler le code de couplage lui-même comme Dans la mesure du possible, la solution idéale consiste à définir chaque énumération séparément et à fournir des packages communs pour toutes les énumérations qui sont fréquemment utilisées ensemble. Les seules énumérations définies dans le même fichier seront celles qui sont intrinsèquement liées entre elles, de sorte que l'utilisation de l'une nécessite également l'utilisation de l'autre.
la source