Sources d'inspiration
Ce billet a vu le jour grâce à une récente publication d'
Aras Prantskevichus sur un rapport destiné aux programmeurs
débutants . Il explique comment s'adapter aux nouvelles architectures ECS. Aras suit le modèle habituel (
explication ci-dessous ): montre des exemples du terrible code OOP, puis démontre que le modèle relationnel (
mais l'appelle «ECS» plutôt que relationnel ) est une excellente alternative. Je ne critique en aucun cas Aras - je suis un grand fan de son travail et je le félicite pour son excellente présentation! J'ai choisi sa présentation au lieu de centaines d'autres articles sur ECS sur Internet car il a fait des efforts supplémentaires et a publié un référentiel git pour étude en parallèle avec la présentation. Il contient un petit «jeu» simple, utilisé comme exemple de sélection de différentes solutions architecturales. Ce petit projet m'a permis de démontrer mes commentaires sur un matériau spécifique, alors merci, Aras!
Les diapositives Aras sont disponibles ici:
http://aras-p.info/texts/files/2018Academy - ECS-DoD.pdf , et le code est sur github:
https://github.com/aras-p/dod-playground .
Je ne vais pas (encore?) Analyser l'architecture ECS résultante de ce rapport, mais je me concentrerai sur le "mauvais code POO" (similaire à l'astuce farcie) depuis son début. Je vais montrer à quoi cela ressemblerait vraiment si toutes les violations des principes d'OOD (conception orientée objet, conception orientée objet) étaient correctement corrigées.
Spoiler: l'élimination de toutes les violations OOD conduit à des améliorations de performances similaires aux conversions Aras en ECS, il utilise également moins de RAM et nécessite moins de lignes de code que la version ECS!TL; DR: Avant de conclure que OOP suce et ECS, faites une pause et examinez OOD (pour savoir comment utiliser OOP correctement), et comprenez également le modèle relationnel (pour savoir comment appliquer correctement ECS).Je participe à de nombreuses discussions sur ECS sur le forum depuis longtemps, en partie parce que je ne pense pas que ce modèle mérite d'exister en tant que terme distinct (
spoiler: ce n'est qu'une version ad hoc du modèle relationnel ), mais aussi parce que presque
chaque publication, présentation ou article faisant la promotion d'un modèle ECS suit la structure suivante:
- Montrez un exemple de code OOP terrible, dont l'implémentation a de terribles défauts dus à une utilisation excessive de l'héritage (ce qui signifie que cette implémentation viole de nombreux principes d'OOD).
- Montrer que la composition est une meilleure solution que l'héritage (et sans oublier que OOD nous donne en fait la même leçon).
- Montrez que le modèle relationnel est idéal pour les jeux (mais appelez-le «ECS»).
Une telle structure me rend furieux parce que:
(A) c'est une astuce "bourrée" ... elle compare le doux au chaud (mauvais code et bon code) ... et c'est injuste, même si cela est fait involontairement et n'est pas nécessaire pour démontrer que la nouvelle architecture est bonne; et, plus important encore:
(B) elle a un effet secondaire - une telle approche supprime les connaissances et dissuade par inadvertance les lecteurs de se familiariser avec les études menées pendant un demi-siècle. Ils ont commencé à écrire sur le modèle relationnel dans les années 1960. Tout au long des années 70 et 80, ce modèle s'est considérablement amélioré. Les débutants ont souvent des questions comme "dans
quelle classe voulez-vous mettre ces données? ", Et en réponse, on leur dit souvent quelque chose de vague, comme "il
vous suffit d'acquérir de l'expérience et ensuite vous apprenez à comprendre intérieurement " ... mais dans les années 70, cette question était activement étudié et dans le cas général une réponse formelle a été déduite; c'est ce qu'on appelle la
normalisation de la base de données . En ignorant la recherche existante et en qualifiant ECS de solution complètement nouvelle et moderne, vous cachez cette connaissance aux débutants.
Les bases de la programmation orientée objet ont été posées il y a aussi longtemps, sinon plus tôt (
ce style a commencé à être exploré dans les années 1950 )! Cependant, c'est dans les années 1990 que l'orientation objet est devenue à la mode, virale et s'est très vite transformée en paradigme de programmation dominant. L'explosion de popularité de nombreux nouveaux langages OO, y compris Java et le (
version standardisée ) C ++, s'est produite. Cependant, comme cela était dû à un battage médiatique, tout le monde
avait besoin de connaître ce concept de haut niveau pour écrire dans son curriculum vitae, mais seuls quelques-uns y ont vraiment participé. Ces nouveaux langages ont créé les mots clés -
classe ,
virtuel ,
étend ,
implémente - à partir de nombreuses fonctionnalités d'OO, et je pense que c'est pourquoi à ce moment-là OO était divisé en deux entités distinctes qui vivent leur propre vie.
Je ferai référence à l'utilisation de ces fonctionnalités de langage d'inspiration OO comme «
OOP » et à l'utilisation de techniques de conception / architecture inspirées d'OO «
OOD ». Tous ont très vite repris la POO. Les établissements d'enseignement ont des cours OO qui préparent de nouveaux programmeurs OOP ... cependant, la connaissance de l'OOD est à la traîne.
Je crois que le code qui utilise les fonctionnalités de langage d'OOP, mais ne suit pas les principes de conception OOD, n'est
pas un code OO . La plupart des critiques contre OOP utilisent par exemple du code vidé, qui n'est pas vraiment un code OO.
Le code OOP a une très mauvaise réputation, et en particulier parce que la plupart du code OOP ne suit pas les principes de l'OOD, et n'est donc pas un "vrai" code OO.
Contexte
Comme indiqué ci-dessus, les années 1990 sont devenues l'apogée de la «mode OO» et c'est à cette époque que la «mauvaise OOP» était probablement la pire. Si vous avez étudié la POO à ce moment-là, vous avez probablement appris les «quatre piliers de la POO»:
- Abstraction
- Encapsulation
- Polymorphisme
- Héritage
Je préfère les appeler non pas quatre piliers, mais «quatre outils de POO». Ce sont des outils que
vous pouvez utiliser pour résoudre des problèmes. Cependant, il ne suffit pas simplement de savoir comment l'outil fonctionne, vous devez savoir quand l'utiliser ... De la part des enseignants, il est irresponsable d'enseigner aux gens un nouvel outil, de ne pas leur dire quand chacun vaut la peine d'être utilisé. Au début des années 2000, il y avait une résistance à la mauvaise utilisation active de ces outils, une sorte de «deuxième vague» de la pensée OOD. Le résultat a été l'émergence de mnémoniques
SOLIDES , qui ont fourni un moyen rapide d'évaluer les forces architecturales. Il convient de noter que cette sagesse était en fait répandue dans les années 90, mais n'a pas encore reçu un acronyme sympa, ce qui leur a permis d'être fixés en cinq principes de base ...
- Le principe de la responsabilité unique ( principe de responsabilité unique ). Chaque classe ne doit avoir qu'une seule raison pour le changement. Si la classe "A" a deux responsabilités, vous devez créer la classe "B" et "C" pour traiter chacune d'elles individuellement, puis créer "A" à partir de "B" et "C".
- Le principe d'ouverture / fermeture ( O stylo / principe fermé). Le logiciel change au fil du temps ( c'est-à-dire que son support est important ). Essayez de mettre les parties les plus susceptibles de changer dans les implémentations ( c'est-à-dire dans des classes spécifiques ) et de créer des interfaces basées sur les parties qui ne sont pas susceptibles de changer ( par exemple, les classes de base abstraites ).
- Le principe de substitution de Barbara Liskov (principe de substitution de L iskov). Chaque implémentation d'une interface doit répondre à 100% aux exigences de cette interface, c'est-à-dire tout algorithme fonctionnant avec une interface devrait fonctionner avec n'importe quelle implémentation.
- Le principe de séparation de l'interface (principe de ségrégation d' interface ). Rendez les interfaces aussi petites que possible afin que chaque partie du code «connaisse» la plus petite quantité de base de code, par exemple, évite les dépendances inutiles. Cette astuce est également bonne pour C ++, où les temps de compilation deviennent énormes si vous ne les suivez pas.
- Le principe d'inversion de dépendance ( principe d'inversion de dépendance ). Au lieu de deux implémentations spécifiques qui communiquent directement (et dépendent l'une de l'autre), elles peuvent généralement être séparées en formalisant leur interface de communication en tant que troisième classe, utilisée comme interface entre elles. Il peut s'agir d'une classe de base abstraite qui définit les appels des méthodes utilisées entre elles, ou même simplement d'une structure POD qui définit les données transférées entre elles.
- Un autre principe n'est pas inclus dans l'acronyme SOLID, mais je suis sûr qu'il est très important: «Préférez la composition à l'héritage» (Principe de réutilisation composite). La composition est le bon choix par défaut . L'héritage devrait être laissé pour les cas où cela est absolument nécessaire.
Nous obtenons donc SOLID-C (++)

Ci-dessous, je ferai référence à ces principes, en les appelant des acronymes - SRP, OCP, LSP, ISP, DIP, CRP ...
Quelques notes supplémentaires:
- Dans OOD, les concepts d' interfaces et d' implémentations ne peuvent être liés à aucun mot clé OOP spécifique. En C ++, nous créons souvent des interfaces avec des classes de base abstraites et des fonctions virtuelles , puis des implémentations héritent de ces classes de base ... mais ce n'est qu'une façon spécifique d'implémenter le principe de l'interface. En C ++, on peut aussi utiliser PIMPL , pointeurs opaques , typage canard , typedef, etc ... Vous pouvez créer une structure OOD puis l'implémenter en C, dans lequel il n'y a aucun mot-clé de langage OOP! Donc, quand je parle d' interfaces , je ne parle pas nécessairement de fonctions virtuelles - je parle du principe de masquer l'implémentation . Les interfaces peuvent être polymorphes , mais le plus souvent elles le sont! Le polymorphisme est très rarement utilisé correctement, mais les interfaces sont un concept fondamental pour tous les logiciels.
- Comme je l'ai précisé ci-dessus, si vous créez une structure POD qui stocke simplement des données pour la transmission d'une classe à une autre, cette structure est utilisée comme interface - il s'agit d'une description formelle des données .
- Même si vous créez simplement une classe distincte avec les parties publiques et privées , tout ce qui se trouve dans la partie commune est une interface et tout dans la partie privée est une implémentation .
- L'héritage a en fait (au moins) deux types - l'héritage d'interface et l'héritage d'implémentation.
- En C ++, l'héritage d'interface comprend des classes de base abstraites avec des fonctions virtuelles pures, PIMPL, typedef conditionnel. En Java, l'héritage d'interface est exprimé par le mot clé implements .
- En C ++, l'héritage des implémentations se produit chaque fois que les classes de base contiennent autre chose que des fonctions virtuelles pures. En Java, l'héritage d'implémentation est exprimé à l'aide du mot-clé extend .
- OOD a beaucoup de règles pour hériter des interfaces, mais l'héritage des implémentations vaut généralement la peine d'être considéré comme du "code en un rien" !
Et enfin, je devrais montrer quelques exemples de la terrible formation OOP et comment elle conduit à un mauvais code dans la vie réelle (et à la mauvaise réputation d'OOP).
- Lorsque vous avez appris les hiérarchies / héritages, vous pourriez avoir été chargé d'une tâche similaire: supposons que vous ayez une application universitaire contenant un répertoire des étudiants et du personnel. Vous pouvez créer la classe de base Person, puis la classe Student et la classe Staff, héritées de Person.
Non, non, non. Ici, je vais vous arrêter. L'implication tacite du principe LSP est que les hiérarchies de classes et les algorithmes qui les traitent sont symbiotiques. Ce sont les deux moitiés de l'ensemble du programme. La POO est une extension de la programmation procédurale, et elle est encore principalement associée à ces procédures. Si nous ne savons pas quels types d'algorithmes fonctionneront avec les étudiants et le personnel ( et quels algorithmes seront simplifiés en raison du polymorphisme ), il sera complètement irresponsable de commencer à créer la structure des hiérarchies de classes. Vous devez d'abord connaître les algorithmes et les données. - Quand on vous a enseigné les hiérarchies / héritages, on vous a probablement confié une tâche similaire: supposons que vous ayez une classe de formes. Nous avons également des carrés et des rectangles comme sous-classes. Un carré doit-il être un rectangle ou un rectangle un carré?
Il s'agit en fait d'un bon exemple pour démontrer la différence entre l'héritage des implémentations et l'héritage des interfaces.
TL; DR - votre classe OOP vous a dit à quoi ressemblait l'héritage. Votre classe OOD manquante aurait dû vous dire de ne pas l'utiliser 99% du temps!
Concepts d'entité / de composant
Après avoir rempli les conditions préalables, passons au point de départ d'Aras - au soi-disant point de départ d'un «POO typique».
Mais pour commencer, un autre ajout - Aras appelle ce code «POO traditionnel», et je veux m'y opposer. Ce code peut être typique de la POO dans le monde réel, mais, comme les exemples ci-dessus, il viole toutes sortes de principes de base de la PO, il ne doit donc pas du tout être considéré comme traditionnel.
Je vais commencer par le premier commit avant qu'il ne commence à refaire la structure vers ECS:
"Faites-le fonctionner à nouveau sur Windows" 3529f232510c95f53112bbfff87df6bbc6aa1fae
Oui, il est difficile de comprendre immédiatement une centaine de lignes de code, alors commençons progressivement ... Nous avons besoin d'un autre aspect des conditions préalables - il était populaire d'utiliser l'héritage dans les jeux des années 90 pour résoudre tous les problèmes de réutilisation du code. Vous aviez Entity, Extensible Character, extensible Player and Monster, et ainsi de suite ... Ceci est un héritage d'implémentations, comme nous l'avons décrit plus haut (
"code avec un étranglement" ), et il semble qu'il soit juste de commencer par cela, mais en conséquence cela conduit à un très base de code rigide. Parce que OOD a le principe de «composition sur héritage» décrit ci-dessus. Ainsi, dans les années 2000, le principe de «composition sur héritage» est devenu populaire et les développeurs de jeux ont commencé à écrire du code similaire.
Que fait ce code? Eh bien pas bon

En bref,
ce code réimplémente une fonctionnalité existante du langage - la composition en tant que bibliothèque d'exécution, et non en tant que fonctionnalité du langage. Vous pouvez imaginer cela comme si le code créait réellement un nouveau métalangage au-dessus de C ++ et une machine virtuelle (VM) pour exécuter ce métalangage. Dans le jeu de démonstration Aras, ce code n'est pas requis (
nous allons le supprimer complètement bientôt! ) Et ne sert qu'à réduire les performances du jeu d'environ 10 fois.
Mais que fait-il réellement? Il s'agit du concept de "système omposant
E ntity /
C " (
parfois pour une raison quelconque appelé "système omponent E ntity / C " ), mais il est complètement différent du concept de "
E ntity
C Sysstem omponent "(" entity-component-system ") (
qui pour des raisons évidentes n'est jamais appelé" S ystem C omponent S ystem systems ). Il formalise plusieurs principes de la "CE":
- le jeu sera construit à partir de l'absence de fonctionnalités des "Entités" ("Entité") ( dans cet exemple appelé GameObjects), qui se composent de "composants" ("Composant").
- GameObjects implémente le modèle «localisateur de service» - leurs composants enfants seront interrogés par type.
- Les composants savent à quel GameObject ils appartiennent - ils peuvent trouver des composants qui sont au même niveau avec eux en interrogeant le GameObject parent.
- La composition ne peut avoir qu'un niveau (les composants ne peuvent pas avoir leurs propres composants enfants, GameObjects ne peut pas avoir de GameObjects enfants ).
- GameObject ne peut avoir qu'un seul composant de chaque type ( dans certains cadres, c'est une exigence obligatoire, dans d'autres non ).
- Chaque composant change (probablement) au fil du temps d'une manière non spécifiée, donc l'interface contient une "mise à jour de vide virtuel".
- Les GameObjects appartiennent à une scène qui peut exécuter des requêtes sur tous les GameObjects (et donc tous les composants).
Un concept similaire était très populaire dans les années 2000, et malgré ses limites, il s'est avéré être suffisamment flexible pour créer d'innombrables jeux à l'époque et aujourd'hui.
Cependant, cela n'est pas obligatoire. Votre langage de programmation prend déjà en charge la composition en tant que caractéristique du langage - il n'y a pas besoin d'un concept gonflé pour y accéder ... Pourquoi, alors, ces concepts existent-ils? Eh bien, pour être honnête, ils vous permettent d'effectuer
une composition dynamique lors de l'exécution . Au lieu de définir durement les types GameObject dans le code, vous pouvez les charger à partir de fichiers de données. Et c'est très pratique, car cela permet aux concepteurs de jeux / niveaux de créer leurs propres types d'objets ... Cependant, dans la plupart des projets de jeux, il y a très peu de concepteurs et littéralement toute une armée de programmeurs, donc je dirais que c'est une opportunité importante. Pire encore, ce n'est pas le seul moyen d'implémenter une composition au moment de l'exécution! Par exemple, Unity utilise C # comme son «langage de script», et de nombreux autres jeux utilisent ses alternatives, par exemple, Lua - un outil pratique pour les concepteurs peut générer du code C # / Lua pour définir de nouveaux objets de jeu sans avoir besoin d'un tel concept gonflé! Nous allons rajouter cette «fonctionnalité» dans le prochain post, et faire en sorte qu'elle ne nous coûte pas dix fois moins de performances ...
Évaluons ce code selon OOD:
- GameObject :: GetComponent utilise dynamic_cast. La plupart des gens vous diront que dynamic_cast est un «code avec un étranglement», un gros indice que vous avez un bug quelque part. Je dirais ceci - c'est la preuve que vous avez violé le LSP - vous avez une sorte d'algorithme qui fonctionne avec l'interface de base, mais il doit connaître différents détails d'implémentation. Pour cette raison particulière, le code sent mauvais.
- GameObject, en principe, n'est pas mauvais, si vous imaginez qu'il implémente le modèle «localisateur de service» ... mais si vous allez plus loin que la critique du point de vue d'OOD, ce modèle crée des connexions implicites entre les parties du projet, et je pense ( sans lien vers Wikipedia qui peut prendre en charge moi avec des connaissances en informatique ) que les canaux de communication implicites sont un contre- modèle , et ils devraient préférer les canaux de communication explicites. Le même argument s'applique au "concept d'événements" gonflé qui est parfois utilisé dans les jeux ...
- Je veux déclarer qu'un composant est une violation de SRP parce que son interface ( Virtual void Update (time) ) est trop large. L'utilisation de «Virtual void Update» dans le développement de jeux est omniprésente, mais je dirais également qu'elle est anti-modèle. Un bon logiciel devrait vous permettre de penser facilement au flux de contrôle et au flux de données. Placer chaque élément du code de gameplay derrière l'appel "Virtual void Update" obscurcit complètement et complètement le flux de contrôle et le flux de données. À mon humble avis, les effets secondaires invisibles, également appelés effets à longue portée , sont quelques-unes des sources les plus courantes de bogues, et la «mise à jour du vide virtuel» garantit que presque tout sera un effet secondaire invisible.
- Bien que l'objectif de la classe Component soit d'activer la composition, elle le fait par héritage, ce qui constitue une violation de CRP .
- Le seul bon côté de cet exemple est que le code de jeu est exagéré afin de se conformer aux principes de SRP et ISP - il est divisé en de nombreux composants simples avec très peu de responsabilité, ce qui est idéal pour réutiliser le code.
Cependant, il n'est pas si bon à maintenir DIP - de nombreux composants ont une connaissance directe les uns des autres.
Ainsi, tout le code ci-dessus peut être supprimé. Toute cette structure. Supprimez GameObject (dans d'autres frameworks également appelés Entity), supprimez Component, supprimez FindOfType. Cela fait partie d'une machine virtuelle inutile qui viole les principes OOD et ralentit terriblement notre jeu.
Composition sans frameworks (c'est-à-dire en utilisant les fonctionnalités du langage de programmation lui-même)
Si nous supprimons le cadre de composition et que nous n'avons pas la classe de base Composant, comment nos GameObjects parviendront-ils à utiliser la composition et à se composer de composants? Comme le titre l'indique, au lieu d'écrire cette machine virtuelle gonflée et de créer des GameObjects dans un étrange métalangage par dessus, écrivons-les simplement en C ++ parce que nous sommes des programmeurs de jeux et c'est littéralement notre travail.
Voici le commit qui a supprimé le framework Entity / Component:
https://github.com/hodgman/dod-playground/commit/f42290d0217d700dea2ed002f2f3b1dc45e8c27cVoici la version originale du code source:
https://github.com/hodgman/dod-playground/blob/3529f232510c95f53112bbfff87df6bbc6aa1fae/source/game.cppVoici la version modifiée du code source:
https://github.com/hodgman/dod-playground/blob/f42290d0217d700dea2ed002f2f3b1dc45e8c27c/source/game.cppEn bref sur les changements:
- Suppression de ": Public Component" de chaque type de composant.
- Ajout d'un constructeur à chaque type de composant.
- OOD consiste principalement à encapsuler l'état d'une classe, mais comme ces classes sont si petites / simples, il n'y a vraiment rien à cacher: l'interface est une description des données. Cependant, l'une des principales raisons pour lesquelles l'encapsulation est le pilier principal est qu'elle nous permet de garantir la vérité constante des invariants de classe ... ou si l'invariant est cassé, il vous suffit d'examiner le code d'implémentation encapsulé pour trouver l'erreur. Dans cet exemple de code, il vaut la peine d'ajouter des constructeurs pour implémenter un simple invariant - toutes les valeurs doivent être initialisées.
- J'ai renommé les méthodes «générales» trop générales pour que leurs noms reflètent ce qu'elles font réellement - UpdatePosition pour MoveComponent et ResolveCollisions pour EviterComponent.
- J'ai supprimé trois blocs de code codés en dur qui ressemblaient à un modèle / préfabriqué - le code qui crée un GameObject contenant des types spécifiques de composant, et je l'ai remplacé par trois classes C ++.
- Élimination de la "mise à jour du vide virtuel".
- Au lieu que les composants se recherchent via le modèle de «localisateur de services», le jeu les lie explicitement lors de la construction.
Les objets
Par conséquent, au lieu de ce code "machine virtuelle":
Nous avons maintenant du code C ++ normal:
struct RegularObject { PositionComponent pos; SpriteComponent sprite; MoveComponent move; AvoidComponent avoid; RegularObject(const WorldBoundsComponent& bounds) : move(0.5f, 0.7f)
Des algorithmes
Un autre changement majeur a été apporté aux algorithmes. Rappelez-vous, au début, j'ai dit que les interfaces et les algorithmes fonctionnent en symbiose et devraient s'influencer mutuellement. Ainsi, l'anti-modèle "
Virtual void Update " est devenu l'ennemi ici aussi. Le code initial contient l'algorithme de boucle principale, composé uniquement de ceci:
Vous pouvez dire que c'est beau et simple, mais à mon humble avis, c'est très, très mauvais. Cela obscurcit complètement le
flux de contrôle et le
flux de données dans le jeu. Si nous voulons être en mesure de comprendre notre logiciel, si nous voulons le soutenir, si nous voulons lui ajouter de nouvelles choses, l'optimiser, l'exécuter efficacement sur plusieurs cœurs de processeur, alors nous devons comprendre à la fois le flux de contrôle et le flux de données. Par conséquent, "Virtual void Update" doit être mis à feu.
Au lieu de cela, nous avons créé une boucle principale plus explicite, ce qui simplifie considérablement la compréhension du flux de contrôle (le
flux de données qu'il contient est toujours obscurci, mais nous corrigerons cela dans les validations suivantes ).
L'inconvénient de ce style est que pour
chaque nouveau type d'objet ajouté au jeu, nous devons ajouter plusieurs lignes à la boucle principale. J'y reviendrai dans un prochain article de cette série.
Performances
Il y a beaucoup d'énormes violations d'OOD, de mauvaises décisions sont prises lors du choix d'une structure, et il existe de nombreuses opportunités d'optimisation, mais j'y reviendrai dans le prochain post de la série. Cependant, déjà à ce stade, il est clair que la version avec "OOD fixe" correspond presque complètement ou gagne le code final "ECS" à la fin de la présentation ... Et tout ce que nous avons fait, c'est simplement prendre le mauvais code pseudo-OOP et le faire respecter les principes OOP (et aussi supprimé une centaine de lignes de code)!
Prochaines étapes
Ici, je veux considérer un éventail beaucoup plus large de problèmes, y compris la résolution des problèmes OOD restants, les objets immuables (
programmation dans un style fonctionnel ) et les avantages qu'ils peuvent apporter dans les discussions sur les flux de données, le passage de messages, l'application de la logique DOD à notre code OOD, appliquer la sagesse pertinente dans le code OOD, supprimer ces classes «d'entités» avec lesquelles nous nous sommes retrouvés et utiliser uniquement des composants purs, utiliser différents styles pour connecter les composants (comparer les pointeurs et la responsabilité de l' exécution) des composants de conteneurs du monde réel, la version ECS-révision pour une meilleure optimisation, ainsi que une optimisation plus poussée, ne figure pas dans le rapport Aras
(comme le multi-threading / SIMD). L'ordre ne sera pas nécessairement celui-ci, et je ne considérerai peut-être pas tout ce qui précède ...
Addition
Les liens vers l'article se sont répandus au-delà des cercles des développeurs de jeux, donc j'ajouterai: "
ECS " (
cet article Wikipedia est mauvais, en passant, il combine les concepts d'EC et d'ECS, et ce n'est pas la même chose ... ) - c'est un faux modèle circulant au sein des communautés développeurs de jeux. En fait, il s'agit d'une version du modèle relationnel dans lequel les «entités» ne sont que des identifiants qui désignent un objet informe, les «composants» sont des lignes dans des tableaux spécifiques qui font référence aux identifiants et les «systèmes» sont du code procédural qui peut modifier les composants . Ce «modèle» a toujours été positionné comme une solution au problème de l'application excessive de l'héritage, mais il n'est pas mentionné qu'une application excessive de l'héritage viole en fait les recommandations de la POO. D'où mon indignation. Ce n'est pas la «seule vraie façon» d'écrire des logiciels. Le message est conçu pour s'assurer que les gens apprennent réellement les principes de conception existants.