Il craignait que le grand nombre de cours ne se traduise par un cauchemar de maintenance. À mon avis, cela aurait exactement l'effet inverse.
Je suis absolument du côté de votre ami, mais cela pourrait être une question de nos domaines et des types de problèmes et de conceptions que nous abordons et en particulier des types de choses susceptibles de nécessiter des changements à l'avenir. Différents problèmes, différentes solutions. Je ne crois pas au bien ou au mal, juste aux programmeurs qui essaient de trouver la meilleure façon de résoudre au mieux leurs problèmes de conception particuliers. Je travaille dans VFX qui n'est pas trop différent des moteurs de jeux.
Mais le problème avec lequel je me suis débattu dans ce qui pourrait au moins être appelé un peu plus une architecture conforme à SOLID (elle était basée sur COM), pourrait se résumer grossièrement à "trop de classes" ou "trop de fonctions" comme votre ami pourrait décrire. Je dirais spécifiquement: "trop d'interactions, trop d'endroits qui pourraient éventuellement mal se comporter, trop d'endroits qui pourraient éventuellement provoquer des effets secondaires, trop d'endroits qui pourraient devoir changer, et trop d'endroits qui pourraient ne pas faire ce que nous pensons qu'ils font . "
Nous avions une poignée d'interfaces abstraites (et pures) implémentées par une cargaison de sous-types, comme ça (faites ce diagramme dans le contexte de parler des avantages ECS, ne tenez pas compte du commentaire en bas à gauche):
Où une interface de mouvement ou une interface de nœud de scène peut être implémentée par des centaines de sous-types: lumières, caméras, maillages, solveurs physiques, shaders, textures, os, formes primitives, courbes, etc. etc. (et il y avait souvent plusieurs types de chacun ). Et le problème ultime était vraiment que ces conceptions n'étaient pas si stables. Nous avions des exigences changeantes et parfois les interfaces elles-mêmes devaient changer, et quand vous voulez changer une interface abstraite implémentée par 200 sous-types, c'est un changement extrêmement coûteux. Nous avons commencé à atténuer cela en utilisant des classes de base abstraites entre les deux, ce qui a réduit les coûts de ces modifications de conception, mais elles étaient toujours coûteuses.
J'ai donc commencé à explorer l'architecture de système à composants d'entité utilisée assez couramment dans l'industrie du jeu. Cela a tout changé pour ressembler à ceci:
Et wow! C'était une telle différence en termes de maintenabilité. Les dépendances ne coulaient plus vers les abstractions , mais vers les données (composants). Et dans mon cas au moins, les données étaient beaucoup plus stables et plus faciles à obtenir en termes de conception dès le départ malgré les exigences changeantes (bien que ce que nous pouvons faire avec les mêmes données change constamment avec l'évolution des exigences).
De plus, étant donné que les entités d'un ECS utilisent la composition au lieu de l'héritage, elles n'ont en fait pas besoin de contenir de fonctionnalité. Ils ne sont que le "conteneur de composants" analogique. Cela a fait en sorte que les 200 sous-types analogiques qui ont implémenté une interface de mouvement se transforment en 200 instances d' entité (pas des types séparés avec un code séparé) qui stockent simplement un composant de mouvement (qui n'est rien d'autre que des données associées au mouvement). A PointLight
n'est plus une classe / sous-type distinct. Ce n'est pas du tout une classe. C'est une instance d'une entité qui combine simplement certains composants (données) liés à sa position dans l'espace (mouvement) et aux propriétés spécifiques des lumières ponctuelles. La seule fonctionnalité qui leur est associée se trouve à l'intérieur des systèmes, comme leRenderSystem
, qui recherche des composants lumineux dans la scène pour déterminer comment rendre la scène.
Avec l'évolution des exigences dans le cadre de l'approche ECS, il n'était souvent nécessaire de changer qu'un ou deux systèmes fonctionnant sur ces données ou simplement d'introduire un nouveau système sur le côté, ou d'introduire un nouveau composant si de nouvelles données étaient nécessaires.
Donc pour mon domaine au moins, et je suis presque certain que ce n'est pas pour tout le monde, cela a rendu les choses tellement plus faciles parce que les dépendances coulaient vers la stabilité (des choses qui n'avaient pas besoin de changer souvent du tout). Ce n'était pas le cas dans l'architecture COM lorsque les dépendances circulaient uniformément vers les abstractions. Dans mon cas, il est beaucoup plus facile de déterminer quelles données sont nécessaires pour le mouvement initial plutôt que toutes les choses possibles que vous pourriez en faire, ce qui change souvent un peu au fil des mois ou des années à mesure que de nouvelles exigences arrivent.
Y a-t-il des cas dans la POO où certains ou tous les principes SOLID ne se prêtent pas au nettoyage du code?
Eh bien, un code propre, je ne peux pas le dire, car certaines personnes assimilent le code propre à SOLID, mais il y a certainement des cas où la séparation des données des fonctionnalités comme le fait ECS et la redirection des dépendances des abstractions vers les données peuvent certainement rendre les choses beaucoup plus faciles à changer, pour des raisons évidentes de couplage, si les données vont être beaucoup plus stables que les abstractions. Bien sûr, les dépendances aux données peuvent rendre la maintenance des invariants difficile, mais ECS a tendance à atténuer cela au minimum avec l'organisation du système qui minimise le nombre de systèmes qui accèdent à n'importe quel type de composant.
Ce n'est pas nécessairement que les dépendances devraient se diriger vers les abstractions comme le suggère DIP; les dépendances devraient se diriger vers des choses qui ne nécessiteront probablement pas de changements futurs. Cela peut ou non être des abstractions dans tous les cas (ce n'était certainement pas dans le mien).
- Oui, il existe des principes de conception OOP qui entrent partiellement en conflit avec SOLID
- Oui, il existe des principes de conception OOP qui entrent complètement en conflit avec SOLID.
Je ne sais pas si ECS est vraiment une saveur de POO. Certaines personnes le définissent de cette façon, mais je le vois comme très différent par nature avec les caractéristiques de couplage et la séparation des données (composants) de la fonctionnalité (systèmes) et le manque d'encapsulation des données. Si elle doit être considérée comme une forme de POO, je pense qu'elle est très en conflit avec SOLID (au moins les idées les plus strictes de SRP, ouvert / fermé, substitution de liskov et DIP). Mais j'espère que c'est un exemple raisonnable d'un cas et d'un domaine où les aspects les plus fondamentaux de SOLID, au moins tels que les gens les interpréteraient généralement dans un contexte de POO plus reconnaissable, pourraient ne pas être aussi applicables.
Teeny Classes
J'expliquais l'architecture d'un de mes jeux qui, à la surprise de mon ami, contenait de nombreuses petites classes et plusieurs couches d'abstraction. J'ai soutenu que c'était le résultat de ma concentration sur le fait de donner à tout une responsabilité unique et aussi de desserrer le couplage entre les composants.
L'ECS a beaucoup défié et changé mes vues. Comme vous, je pensais que l'idée même de la maintenabilité est d'avoir l'implémentation la plus simple possible des choses, ce qui implique beaucoup de choses, et en plus, beaucoup de choses interdépendantes (même si les interdépendances sont entre les abstractions). Cela a plus de sens si vous zoomez sur une seule classe ou fonction pour vouloir voir l'implémentation la plus simple et la plus simple, et si nous n'en voyons pas, refactorisez-la et peut-être même décomposez-la davantage. Mais il peut être facile de passer à côté de ce qui se passe avec le monde extérieur, car chaque fois que vous divisez quelque chose de relativement complexe en 2 choses ou plus, ces 2 choses ou plus doivent inévitablement interagir * (voir ci-dessous) les unes avec les autres dans certains façon, ou quelque chose à l'extérieur doit interagir avec chacun d'eux.
Ces jours-ci, je trouve qu'il y a un équilibre entre la simplicité de quelque chose et combien de choses il y a et combien d'interaction est nécessaire. Les systèmes dans un ECS ont tendance à être assez lourds avec des implémentations non triviales pour fonctionner sur les données, comme PhysicsSystem
ou RenderSystem
ou GuiLayoutSystem
. Cependant, le fait qu'un produit complexe en ait besoin si peu a tendance à faciliter le recul et à raisonner sur le comportement global de la base de code entière. Il y a quelque chose là-dedans qui pourrait suggérer que ce ne serait pas une mauvaise idée de se pencher du côté de classes moins nombreuses et plus volumineuses (exerçant toujours une responsabilité sans doute singulière), si cela signifie moins de classes à maintenir et à raisonner, et moins d'interactions tout au long le système.
Les interactions
Je dis «interactions» plutôt que «couplage» (bien que réduire les interactions implique de réduire les deux), car vous pouvez utiliser des abstractions pour découpler deux objets concrets, mais ils se parlent toujours. Ils pourraient encore provoquer des effets secondaires dans le processus de cette communication indirecte. Et souvent, je trouve que ma capacité à raisonner sur l'exactitude d'un système est plus liée à ces «interactions» qu'à un «couplage». Minimiser les interactions a tendance à rendre les choses beaucoup plus faciles pour moi de raisonner sur tout, à vol d'oiseau. Cela signifie que les choses ne se parlent pas du tout, et de ce sens, ECS a également tendance à vraiment minimiser les "interactions", et pas seulement le couplage, au minimum le plus strict (au moins, je n'ai pas '
Cela dit, cela pourrait être au moins partiellement moi et mes faiblesses personnelles. J'ai trouvé le plus grand obstacle pour moi de créer des systèmes à grande échelle, et de raisonner en toute confiance, de les parcourir et d'avoir l'impression que je peux apporter les changements souhaités potentiels n'importe où et de manière prévisible, c'est la gestion des états et des ressources ainsi que Effets secondaires. C'est le plus grand obstacle qui commence à surgir lorsque je passe de dizaines de milliers de LOC à des centaines de milliers de LOC à des millions de LOC, même pour du code que j'ai entièrement écrit moi-même. Si quelque chose va me ralentir par-dessus tout, c'est ce sentiment que je ne peux plus comprendre ce qui se passe en termes d'état d'application, de données, d'effets secondaires. Il' Ce n'est pas le temps robotique nécessaire pour effectuer un changement qui me ralentit autant que l'incapacité à comprendre tous les impacts du changement si le système dépasse la capacité de mon esprit à en raisonner. Et réduire les interactions a été, pour moi, le moyen le plus efficace de permettre au produit de grandir beaucoup plus avec beaucoup plus de fonctionnalités sans que je ne sois personnellement submergé par ces choses, car réduire les interactions au minimum réduit également le nombre d'endroits qui peuvent même éventuellement modifier l'état d'application et provoquer des effets secondaires substantiels.
Il peut tourner quelque chose comme ça (où tout dans le diagramme a des fonctionnalités, et évidemment un scénario du monde réel aurait beaucoup, plusieurs fois le nombre d'objets, et c'est un diagramme "d'interaction", pas un couplage, comme un couplage on aurait des abstractions entre les deux):
... à cela où seuls les systèmes ont des fonctionnalités (les composants bleus ne sont plus que des données, et maintenant c'est un schéma de couplage):
Et il y a des réflexions qui émergent sur tout cela et peut-être un moyen d'encadrer certains de ces avantages dans un contexte de POO plus conforme qui est plus compatible avec SOLID, mais je n'ai pas encore tout à fait trouvé les conceptions et les mots, et je le trouve difficile car la terminologie que j'avais l'habitude de lancer concernait directement la POO. Je continue d'essayer de le comprendre en lisant les réponses des gens ici et en faisant de mon mieux pour formuler les miennes, mais il y a des choses très intéressantes sur la nature d'un ECS que je n'ai pas réussi à placer parfaitement mon doigt dessus pourrait être plus largement applicable même aux architectures qui ne l'utilisent pas. J'espère également que cette réponse ne se présente pas comme une promotion ECS! Je trouve cela très intéressant car la conception d'un ECS a vraiment changé radicalement mes pensées,