Découplage
Il s’agit en fin de compte de me dissocier en fin de journée au niveau de conception le plus fondamental, dépourvu de la nuance des caractéristiques de nos compilateurs et de nos lieurs. Je veux dire que vous pouvez faire des choses comme faire en sorte que chaque en-tête définisse une seule classe, utiliser pimpls, transmettre des déclarations à des types qui doivent seulement être déclarés, non définis, peut-être même utiliser des en-têtes ne contenant que des déclarations en aval (ex:) <iosfwd>
, un en-tête par fichier source , organisez le système de manière cohérente en fonction du type de chose déclarée / définie, etc.
Techniques pour réduire les "dépendances au moment de la compilation"
Certaines techniques peuvent aider, mais vous pouvez épuiser ces pratiques tout en trouvant que votre fichier source moyen dans votre système nécessite un préambule de deux pages. #include
Si vous vous concentrez trop sur la réduction des dépendances au moment de la compilation au niveau de l'en-tête sans réduire les dépendances logiques dans la conception de vos interfaces, les directives ne font rien de vraiment significatif avec des temps de construction en flèche, et si cela peut ne pas être considéré à proprement parler comme un "en-tête de spaghetti", je Je dirais toujours que cela se traduit par des problèmes préjudiciables similaires à la productivité dans la pratique. À la fin de la journée, si vos unités de compilation requièrent toujours une batterie d'informations visibles pour pouvoir faire quoi que ce soit, elles se traduiront par des temps de construction de plus en plus longs et multiplieront les raisons pour lesquelles vous devez potentiellement revenir en arrière et devoir changer les choses tout en faisant en sorte que les développeurs ils ont l’impression d’être en train d’amorcer le système en essayant de terminer leur codage quotidien. Il'
Vous pouvez, par exemple, faire en sorte que chaque sous-système fournisse un fichier d'en-tête et une interface très abstraits. Mais si les sous-systèmes ne sont pas découplés les uns des autres, vous obtenez à nouveau quelque chose qui ressemble à spaghetti avec des interfaces de sous-système dépendant d'autres interfaces de sous-système avec un graphe de dépendance qui ressemble à un gâchis pour fonctionner.
Déclarations de transfert vers des types externes
De toutes les techniques que j'ai épuisées pour essayer d'obtenir une ancienne base de code qui prenait deux heures à construire tandis que les développeurs attendaient parfois deux jours pour leur tour chez CI sur nos serveurs de build (vous pouvez presque imaginer ces machines de construction comme des bêtes de somme épuisées essayant frénétiquement pour suivre et échouer pendant que les développeurs poussent leurs modifications), le plus douteux pour moi était les types de déclaration en aval définis dans d'autres en-têtes. Et j’ai réussi à réduire cette base de code à 40 minutes environ après des siècles d’avoir fait cela petit à petit tout en essayant de réduire les "en-têtes spaghettis", la pratique la plus discutable en rétrospective (comme me faire perdre de vue la nature fondamentale de conception alors que tunnel envisageait les interdépendances d’en-têtes) déclarait en aval les types définis dans d’autres en-têtes.
Si vous imaginez un en- Foo.hpp
tête qui a quelque chose comme:
#include "Bar.hpp"
Et il utilise uniquement Bar
dans l'en-tête une manière qui nécessite sa déclaration, pas sa définition. alors, cela peut sembler une évidence de dire class Bar;
d'éviter de rendre Bar
visible la définition de dans l'en-tête. Excepté dans la pratique, vous constaterez souvent que la plupart des unités de compilation utilisées Foo.hpp
doivent encore Bar
être définies avec le fardeau supplémentaire de devoir s’inclure par Bar.hpp
-dessus Foo.hpp
, ou que vous rencontrez un autre scénario dans lequel cela aide réellement. % de vos unités de compilation peuvent travailler sans inclure Bar.hpp
, sauf que cela soulève la question plus fondamentale de la conception (ou du moins, je pense que cela devrait être le cas de nos jours): pourquoi elles ont même besoin de voir la déclaration de Bar
et pourquoiFoo
il faut même être dérangé de le savoir si cela n’est pas pertinent pour la plupart des cas d’utilisation (pourquoi alourdir une conception avec des dépendances les unes par rapport aux autres)?
Parce que conceptuellement, nous ne sommes pas vraiment découplés Foo
de Bar
. Nous venons de faire en sorte que l'en-tête de Foo
ne nécessite pas autant d'informations sur l'en-tête de Bar
, ce qui est loin d'être aussi substantiel qu'un design qui rend véritablement ces deux éléments complètement indépendants l'un de l'autre.
Script intégré
C’est vraiment pour les bases de code à plus grande échelle, mais une autre technique que je trouve extrêmement utile consiste à utiliser un langage de script intégré pour au moins les parties les plus avancées de votre système. J'ai découvert que j'étais capable d'intégrer Lua en une journée et de l'avoir uniformément capable d'appeler toutes les commandes de notre système (les commandes étaient abstraites, heureusement). Malheureusement, je suis tombé sur un barrage routier où les développeurs se méfiaient de l'introduction d'une autre langue et, ce qui est peut-être le plus étrange, de la performance, leur principal soupçon. Bien que je puisse comprendre d’autres préoccupations, les performances ne devraient pas être un problème si nous n’utilisons le script que pour appeler des commandes lorsque les utilisateurs cliquent sur des boutons, par exemple, n’effectuant aucune boucle lourde (ce que nous essayons de faire, vous inquiétez-vous des différences de nanosecondes dans les temps de réponse pour un clic de bouton?).
Exemple
Entre-temps, le moyen le plus efficace que je connaisse après avoir épuisé les techniques de réduction du temps de compilation dans les bases de code volumineuses consiste en des architectures qui réduisent réellement la quantité d'informations requises pour le fonctionnement d'un élément du système, et non pas simplement le découplage d'un en-tête d'un compilateur. perspective mais en demandant aux utilisateurs de ces interfaces de faire ce qu’ils doivent faire tout en sachant (du point de vue humain et du compilateur, un véritable découplage qui va au-delà des dépendances du compilateur) le strict minimum.
L’ECS n’est qu’un exemple (et je ne vous suggère pas d’en utiliser un), mais sa découverte m’a montré que vous pouvez avoir des bases de code vraiment épiques qui construisent toujours étonnamment rapidement tout en utilisant avec bonheur des modèles et beaucoup d’autres bienfaits, car ECS, par nature, crée une architecture très découplée où les systèmes ont seulement besoin de connaître la base de données ECS et en général seulement quelques types de composants (parfois un seul) pour faire leur travail:
Design, Design, Design
Et ces types de conceptions architecturales découplées au niveau humain et conceptuel sont plus efficaces en termes de réduction des temps de compilation que toutes les techniques que j'ai expliquées ci-dessus à mesure que votre base de code grandit et grandit et grandit, car cette croissance ne correspond pas à votre moyenne. unité de compilation en multipliant la quantité d’information requise lors de la compilation et les temps de liaison pour fonctionner (tout système qui oblige votre développeur moyen à inclure un paquet de données pour faire quoi que ce soit exige de la part du compilateur, et pas seulement du compilateur à connaître beaucoup d’informations pour faire quoi que ce soit ). Il présente également plus d'avantages que des temps de construction réduits et des en-têtes non démêlés, car cela signifie également que vos développeurs n'ont pas besoin d'en savoir plus sur le système, au-delà de ce qui est immédiatement requis pour en faire quelque chose.
Si, par exemple, vous pouvez engager un développeur expert en physique pour développer un moteur physique pour votre jeu AAA qui couvre des millions de LOC, il peut démarrer très rapidement tout en connaissant le minimum d'informations absolues en ce qui concerne les types et les interfaces disponibles. ainsi que vos concepts de système, alors cela va naturellement se traduire par une quantité réduite d'informations pour lui et le compilateur, ce qui se traduira également par une réduction considérable des temps de construction tout en impliquant généralement qu'il n'y a rien qui ressemble à des spaghettis n'importe où dans le système. Et c’est ce que je propose d’accorder la priorité à toutes ces autres techniques: la conception de vos systèmes. Épuiser d’autres techniques sera la cerise sur le gâteau si vous le faites pendant, sinon,