Existe-t-il des versions de la POO où certains ou tous les principes SOLID sont antithétiques pour nettoyer le code?

26

J'ai récemment eu une discussion avec un de mes amis sur la POO dans le développement de jeux vidéo.

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 fait valoir que c'était le résultat de ma concentration sur tout donner une responsabilité unique et aussi pour desserrer le couplage entre les composants.

Il craignait que le grand nombre de cours ne se traduise par un cauchemar de maintenance. À mon avis, cela aurait exactement l'effet inverse. Nous avons procédé à la discussion de ce qui semblait être des siècles, finissant par accepter d'être en désaccord, en disant qu'il y avait peut-être des cas où les principes SOLIDES et la POO appropriée ne se mélangeaient pas vraiment bien.

Même l'entrée de Wikipédia sur les principes SOLID déclare qu'il s'agit de lignes directrices qui aident à écrire du code maintenable et qu'elles font partie d'une stratégie globale de programmation agile et adaptative.

Donc, ma question est:

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?

Je peux imaginer tout de suite que le principe de substitution de Liskov pourrait éventuellement entrer en conflit avec une autre saveur de l'héritage sûr. C'est-à-dire que si quelqu'un a conçu un autre modèle utile implémenté par héritage, il est fort possible que le LSP soit en conflit direct avec lui.

Y en a-t-il d'autres? Peut-être que certains types de projets ou certaines plateformes cibles fonctionnent mieux avec une approche moins SOLIDE?

Modifier:

Je voudrais juste préciser que je ne demande pas comment améliorer mon code;) La seule raison pour laquelle j'ai mentionné un projet dans cette question était de donner un peu de contexte. Ma question concerne la POO et les principes de conception en général .

Si vous êtes curieux de mon projet, voyez ceci .

Modifier 2:

J'ai imaginé que cette question recevrait une réponse de 3 manières:

  1. Oui, il existe des principes de conception OOP qui entrent partiellement en conflit avec SOLID
  2. Oui, il existe des principes de conception OOP qui entrent complètement en conflit avec SOLID
  3. Non, SOLID est le genou de l'abeille et OOP s'en sortira toujours mieux. Mais, comme pour tout, ce n'est pas une panacée. Boire de façon responsable.

Les options 1 et 2 auraient probablement généré des réponses longues et intéressantes. L'option 3, en revanche, serait une réponse courte, sans intérêt mais globalement rassurante.

Nous semblons converger vers l'option 3.

MetaFight
la source
Comment définissez-vous le «code propre»?
GrandmasterB
Le code propre, dans le cadre de cette question, est une structure de code qui répond aux objectifs de la POO et à un ensemble de principes de conception choisis. Donc, je connais le code propre en termes des objectifs fixés par OOP + SOLID. Je me demande s'il existe un autre ensemble de principes de conception qui fonctionnent avec OOP mais sont totalement ou partiellement incompatibles avec SOLID.
MetaFight
1
Je m'inquiète beaucoup plus des performances de ce jeu que de vous. Sauf si nous parlons de jeu basé sur du texte.
Euphoric
1
Ce jeu est essentiellement une expérience. J'essaie différentes approches de développement et j'essaie également de voir si le code d'entreprise peut fonctionner lors de l'écriture d'un jeu. Je m'attends à ce que les performances deviennent un problème, mais ce n'est pas encore le cas. Si / quand c'est le cas, alors la prochaine chose que je vais expérimenter est de savoir comment adapter mon code en code performant. Tellement d'apprentissage!
MetaFight
1
Au risque de quelques downvotes, je voudrais revenir à la question "pourquoi": OOP dans le développement de jeux . En raison de la nature du développement du jeu, la POO "traditionnelle" semble tomber en vogue au profit de diverses saveurs de systèmes d'entités / composants, une variante intéressante ici par exemple. Cela me fait plutôt penser que la question ne devrait pas être "SOLID + OOP" mais plutôt "OOP + Game Development". Lorsque vous regardez un graphique de l'interaction des objets pour le développement de jeu. par rapport à une application à N niveaux, les différences peuvent signifier qu'un nouveau paradigme est bon.
J Trana

Réponses:

20

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?

En général, non. L'histoire a montré que les principes SOLID contribuent tous en grande partie à un découplage accru, qui à son tour a montré qu'il augmentait la flexibilité du code et donc votre capacité à s'adapter au changement et à rendre le code plus facile à raisonner, à tester, à réutiliser. En bref, nettoyez votre code.

Maintenant, il peut y avoir des cas où les principes SOLIDES entrent en collision avec DRY (ne vous répétez pas), KISS (gardez les choses stupides) ou d'autres principes de bonne conception OO. Et bien sûr, ils peuvent entrer en collision avec la réalité des exigences, les limites des humains, les limites de nos langages de programmation, d'autres obstacles.

En bref, les principes SOLID se prêteront toujours au code propre, mais dans certains scénarios, ils se prêteront moins que des alternatives conflictuelles. Ils sont toujours bons, mais parfois d'autres choses sont plus bonnes.

Telastyn
la source
14
J'aime mais parfois d'autres choses sont plus bonnes . Il a une sensation de «ferme animale». ;)
FrustratedWithFormsDesigner
12
+1 Quand je viens de regarder les principes SOLID, je vois 5 "sympa à avoir". Mais aucun d'entre eux ne dépasse DRY, KISS et "clarifie vos intentions". Utilisez SOLID, mais faites preuve de prudence et de scepticisme.
user949300
Je suis d'accord. Je pense que le (s) bon (s) principe (s) à suivre dépendent clairement de la situation, mais en dehors des exigences de performances difficiles, qui sont toujours au-dessus de tout le reste (cela importe particulièrement dans le développement de jeux), j'ai tendance à penser que DRY et KISS sont généralement les deux plus importants que SOLIDE. Bien sûr, plus vous nettoyez le code, mieux c'est, donc si vous pouvez suivre tous les principes sans conflits, tant mieux.
Ben Lee
Qu'en est-il de la ségrégation d'intégration par rapport à la conception efficace des agrégats? Si l'interface "séquence" de base [par exemple IEnumerable] comprend des méthodes comme Countet asImmutable, et des propriétés comme getAbilities[dont le retour indiquerait si des choses comme Countseront "efficaces"], alors on pourrait avoir une méthode statique qui prend plusieurs séquences et les agrège pour qu'elles se comportera comme une seule séquence plus longue. Si de telles capacités sont présentes dans le type de séquence de base, même si elles ne se
chaînent
... et les implémenter efficacement lorsqu'ils sont utilisés avec des classes de base qui peuvent le faire. Si l'on agrège une liste de dix millions d'éléments qui connaît son nombre et une liste de cinq éléments qui ne peut récupérer que des éléments séquentiellement, une demande pour la 10 000 003e entrée doit lire le nombre de la première liste, puis lire trois éléments de la seconde . Les interfaces évier-cuisine peuvent violer le FAI, mais elles pourraient considérablement améliorer les performances de certains scénarios de composition / agrégation.
supercat
13

Je pense que j'ai une perspective inhabituelle en ce sens que j'ai travaillé à la fois dans la finance et les jeux.

Beaucoup de programmeurs que j'ai rencontrés dans les jeux étaient terribles en génie logiciel - mais ils n'avaient pas besoin de pratiques comme SOLID. Traditionnellement, ils mettent leur jeu dans une boîte - puis ils ont terminé.

En finance, j'ai trouvé que les développeurs peuvent être très bâclés et indisciplinés car ils n'ont pas besoin des performances que vous faites dans les jeux.

Les deux déclarations ci-dessus sont bien sûr des généralisations excessives, mais en fait, dans les deux cas, un code propre est essentiel. Pour une optimisation, un code clair et facile à comprendre est essentiel. Par souci de maintenabilité, vous voulez la même chose.

Cela ne veut pas dire que SOLID n'est pas sans critique. On les appelle des principes et pourtant ils sont censés être suivis comme des lignes directrices? Alors, quand dois-je les suivre exactement? Quand dois-je enfreindre les règles?

Vous pourriez probablement regarder deux morceaux de code et dire lequel est le mieux compatible avec SOLID. Mais isolément, il n'y a aucun moyen objectif de transformer le code en SOLIDE. C'est définitivement à l'interprétation du développeur.

Il n'est pas logique de dire que "un grand nombre de classes peut conduire à un cauchemar de maintenance". Cependant, si SRP n'est pas correctement interprété, vous pourriez facilement vous retrouver avec ce problème.

J'ai vu du code avec de nombreuses couches à peu près sans responsabilité. Les classes qui ne font rien à part passer l'état d'une classe à l'autre. Le problème classique de trop de couches d'indirection.

SRP en cas d'abus peut se traduire par un manque de cohésion dans votre code. Vous pouvez le savoir lorsque vous essayez d'ajouter une fonctionnalité. Si vous devez toujours changer plusieurs endroits en même temps, votre code manque de cohésion.

Open-Closed n'est pas sans critiques. Par exemple, voir la revue de Jon Skeet du principe Open Closed . Je ne reproduirai pas ses arguments ici.

Votre argument sur LSP semble plutôt hypothétique et je ne comprends pas vraiment ce que vous entendez par une autre forme d'héritage sûr?

ISP semble reproduire quelque peu SRP. Ils doivent certainement être interprétés de la même manière, car vous ne pouvez jamais prévoir ce que feront tous les clients de votre interface. L'interface cohérente d'aujourd'hui est le violeur de FAI de demain. La seule conclusion illogique est de ne pas avoir plus d'un membre par interface.

DIP peut conduire à un code inutilisable. J'ai vu des classes avec un grand nombre de paramètres au nom de DIP. Ces classes auraient normalement "renouvelé" leurs parties composites, mais pour le nom de la capacité de test, nous ne devons plus jamais utiliser le nouveau mot-clé.

SOLID ne suffit pas à lui seul pour écrire du code propre. J'ai vu le livre de Robert C. Martin, « Clean Code: A Handbook of Agile Software Craftsmanship ». Le plus gros problème est qu'il est facile de lire ce livre et de l'interpréter comme des règles. Si vous faites cela, vous manquez le point. Il contient une grande liste d'odeurs, d'heuristiques et de principes - bien plus que les cinq de SOLID. Tous ces «principes» ne sont pas vraiment des principes mais des lignes directrices. L'oncle Bob les décrit lui-même comme des "trous cubiques" pour les concepts. C'est un jeu d'équilibre; suivre aveuglément toute directive entraînera des problèmes.

Dave Hillier
la source
1
Si vous avez un grand nombre de paramètres de constructeur, cela suggère que vous avez violé le SRP. Pourtant, un conteneur DI supprime de toute façon la douleur associée à un grand nombre de paramètres de constructeur, donc je ne suis pas d'accord avec vos déclarations sur DIP.
Stephen
Tous ces «principes» ne sont pas vraiment des principes mais des lignes directrices. C'est un jeu d'équilibre; comme je l'ai dit dans mon article après SRP aussi, l'extrême lui-même peut être problématique.
Dave Hillier
Bonne réponse, +1. Une chose que je voudrais ajouter: j'ai lu la critique de Skeet davantage un critique de la définition standard des manuels de l'OCP, pas des idées de base derrière l'OCP. À ma connaissance, l'idée de variation protégée est ce que l'OCP aurait dû être dès le départ.
Doc Brown
5

Voici mon avis:

Bien que les principes SOLID visent une base de code non redondante et flexible, cela pourrait être un compromis en termes de lisibilité et de maintenance s'il y a trop de classes et de couches.

Il s'agit surtout du nombre d' abstractions . Un grand nombre de classes pourraient être correctes si elles sont basées sur quelques abstractions. Comme nous ne connaissons pas la taille et les spécificités de votre projet, c'est difficile à dire, mais il est un peu inquiétant que vous mentionniez plusieurs couches en plus d'un grand nombre de classes.

Je suppose que les principes SOLIDES ne sont pas en désaccord avec la POO, mais il est toujours possible d'écrire du code non propre en y adhérant.

henginy
la source
3
+1 Par définition presque, un système hautement flexible doit être moins facile à entretenir qu'un système moins flexible. La flexibilité a un coût en raison de la complexité supplémentaire qu'elle apporte.
Andy
@Andy pas nécessairement. dans mon travail actuel, beaucoup des pires parties du code sont en désordre parce qu'il est simplement jeté ensemble en "faisant quelque chose de simple, il suffit de le pirater ensemble et de le faire fonctionner pour qu'il ne devienne pas si complexe". En conséquence, de nombreuses parties du système sont 100% rigides. Vous ne pouvez pas les modifier du tout sans en réécrire de grandes parties. C'est vraiment difficile à maintenir. La maintenabilité vient d'une forte cohésion et d'un faible couplage, et non de la flexibilité du système.
sara
3

Dans mes premiers jours de développement, j'ai commencé à écrire un Roguelike en C ++. Comme je voulais appliquer la bonne méthodologie orientée objet que j'ai apprise de mon éducation, j'ai immédiatement vu les éléments du jeu comme des objets C ++. Potion, Swords, Food, Axe, JavelinsEtc. tous dérivés d'un noyau de base du Itemcode, le nom de manipulation, l' icône, le poids, ce genre de choses.

Ensuite, j'ai codé la logique du sac et j'ai rencontré un problème. Je peux mettre Itemsdans le sac, mais ensuite, si j'en choisis un, comment savoir si c'est un Potionou un Sword? J'ai cherché sur Internet comment abattre des éléments. J'ai piraté les options du compilateur pour activer les informations d'exécution afin que je sache quels sont mes Itemvrais types abstraits , et j'ai commencé à basculer la logique sur les types pour savoir quelles opérations je permettrais (par exemple, éviter de boire Swordset de mettre les Potionspieds)

Il a essentiellement transformé le code en une grosse boule de boue à moitié copypastée. Au fur et à mesure que le projet progressait, j'ai commencé à comprendre quel était l'échec de la conception. Ce qui s'est passé, c'est que j'ai essayé d'utiliser des classes pour représenter des valeurs. Ma hiérarchie de classe n'était pas une abstraction significative. Ce que je aurais dû faire fait de Itemmettre en œuvre un ensemble de fonctions, telles que Equip (), Apply ()et Throw (), et font de chaque se comportent en fonction des valeurs. Utiliser des énumérations pour représenter les emplacements d'équipement. Utiliser des énumérations pour représenter différents types d'armes. Utiliser plus de valeurs et moins de classement, car ma sous-classification n'avait d'autre but que de remplir ces valeurs finales.

Étant donné que vous êtes confronté à la multiplication d'objets sous une couche d'abstraction, je pense que mes informations peuvent être précieuses ici. Si vous semblez avoir trop de types d'objets, il se peut que vous confondiez ce qui devrait être des types avec ce qui devrait être des valeurs.

Arthur Havlicek
la source
2
Le problème était de confondre les types avec les classes de valeurs (remarque: je n'utilise pas le mot classe pour faire référence à la notion de classe OOP / C ++.) Il existe plusieurs façons de représenter un point dans un plan cartésien (coordonnées rectangulaires, polaires coordonnées) mais ils font tous partie du même type. Cependant, les langages OOP traditionnels ne prennent pas en charge la classification des valeurs naturellement. La chose la plus proche de cela est l'union de C / C ++, mais vous devez ajouter un champ supplémentaire juste pour savoir ce que vous y mettez.
Doval
4
Votre expérience peut être utilisée comme anti-exemple. En n'utilisant pas SOLID ni aucune sorte de méthodologie de modélisation, vous avez créé du code non maintenable et non extensible.
Euphoric
1
@ Euphoric, j'ai essayé dur. J'avais une méthodologie orientée objet. Il y a une différence entre avoir une méthodologie et la mettre en œuvre avec succès, cependant :)
Arthur Havlicek
2
En quoi est-ce un exemple de principes SOLIDES allant à l'encontre du code propre dans la POO? Cela ressemble plus à un exemple de conception incorrecte - c'est orthogonal à la POO!
Andres F.
1
Votre classe ITEM de base devrait avoir eu un certain nombre de méthodes booléennes "canXXXX", toutes par défaut à FALSE. Vous pouvez ensuite définir «canThrow ()» pour renvoyer true dans votre classe Javelin et canEat () pour renvoyer true pour votre classe Postion.
James Anderson
3

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):

entrez la description de l'image ici

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:

entrez la description de l'image ici

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 PointLightn'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.

entrez la description de l'image ici

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).

  1. Oui, il existe des principes de conception OOP qui entrent partiellement en conflit avec SOLID
  2. 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 PhysicsSystemou RenderSystemou 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):

entrez la description de l'image ici

... à 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):

entrez la description de l'image ici

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,


la source
J'aime la distinction entre interactions et couplage. Bien que votre premier diagramme d'interaction soit obscurci. C'est un graphe planaire, donc pas besoin de croiser les flèches.
D Drmmr
2

Les principes SOLIDES sont bons, mais KISS et YAGNI sont prioritaires en cas de conflits. Le but de SOLID est de gérer la complexité, mais si appliquer SOLID lui-même rend le code plus complexe, il va à l'encontre du but. Pour prendre un exemple, si vous avez un petit programme avec seulement quelques classes, l'application de DI, la ségrégation d'interface, etc. pourrait très bien augmenter la complexité globale du programme.

Le principe ouvert / fermé est particulièrement controversé. Il dit essentiellement que vous devez ajouter des fonctionnalités à une classe en créant une sous-classe ou une implémentation distincte de la même interface, mais laisser la classe d'origine intacte. Si vous gérez une bibliothèque ou un service utilisé par des clients non coordonnés, cela a du sens, car vous ne voulez pas casser le comportement sur lequel le client sortant peut compter. Cependant, si vous modifiez une classe qui n'est utilisée que dans votre propre application, il serait souvent beaucoup plus simple et plus propre de simplement modifier la classe.

JacquesB
la source
Votre discussion sur l'OCP ignore la possibilité d'étendre le comportement d'une classe via la composition (par exemple en utilisant un objet de stratégie pour permettre à un algorithme utilisé par la classe d'être modifié), ce qui est généralement considéré comme un meilleur moyen de se conformer au principal que le sous-classement. .
Jules
1
Si KISS et YAGNI ont toujours eu la priorité, vous devriez toujours coder en 1 et en 0. Les assembleurs sont juste autre chose qui peut mal tourner. Aucun KISS et YAGNI ne signalent deux des nombreux coûts de conception. Ce coût doit toujours être mis en balance avec les avantages de tout travail de conception. SOLID présente des avantages qui compensent dans certains cas les coûts. Il n'y a pas de moyen simple de savoir quand vous avez franchi la ligne.
candied_orange
@CandiedOrange: Je ne suis pas d'accord pour dire que le code machine est plus simple que le code dans un langage de haut niveau. Le code machine finit par contenir beaucoup de complexité accidentelle en raison du manque d'abstraction, il n'est donc certainement pas KISS de décider d'écrire une application typique en code machine.
JacquesB
@Jules: Oui, la composition est préférable, mais cela vous oblige toujours à concevoir la classe d'origine de telle sorte que la composition puisse être utilisée pour la personnaliser, vous devez donc faire des hypothèses sur les aspects qui doivent être personnalisés à l'avenir et quels aspects ne sont pas personnalisables. Dans certains cas, cela est nécessaire (par exemple, vous écrivez une bibliothèque pour la distribution) mais cela a un coût, donc suivre SOLID n'est pas toujours optimal.
JacquesB
1
@JacquesB - vrai. Et OCP est fondamentalement opposé à YAGNI; quelle que soit la manière dont vous y parvenez, il vous faut concevoir des points d'extension pour permettre une amélioration ultérieure. Trouver un équilibre approprié entre les deux idéaux est ... délicat.
Jules
1

Dans mon expérience:-

Les grandes classes sont exponentiellement plus difficiles à maintenir à mesure qu'elles grandissent.

Les petites classes sont plus faciles à maintenir et la difficulté de maintenir une collection de petites classes augmente arithmétiquement au nombre de classes.

Parfois, de grandes classes sont inévitables, un gros problème a parfois besoin d'une grande classe, mais elles sont beaucoup plus difficiles à lire et à comprendre. Pour les classes plus petites bien nommées, le nom peut suffire à la compréhension, vous n'avez même pas besoin de regarder le code, sauf s'il y a un problème très spécifique avec la classe.

James Anderson
la source
0

J'ai toujours du mal à tracer la ligne entre le «S» du solide et le besoin (peut-être à l'ancienne) de laisser un objet avoir toute la connaissance de lui-même.

Il y a 15 ans, j'ai travaillé avec des développeurs Deplhi qui avaient leur Saint Graal pour avoir des cours qui contenaient tout. Donc, pour une hypothèque, vous pouvez ajouter des assurances et des paiements et des revenus et des prêts et des informations fiscales et vous pouvez calculer l'impôt à payer, l'impôt à déduire et il peut se sérialiser, persister sur le disque, etc. etc.

Et maintenant, j'ai le même objet divisé en beaucoup, beaucoup de classes plus petites qui ont l'avantage de pouvoir être testées unitaire beaucoup plus facilement et mieux, mais ne donnent pas un bon aperçu de l'ensemble du modèle d'entreprise.

Et oui, je sais que vous ne voulez pas lier vos objets à la base de données, mais vous pouvez injecter un référentiel dans la grande classe hypothécaire pour résoudre ce problème.

En plus de cela, il n'est pas toujours facile de trouver de bons noms pour les classes qui ne font qu'une petite chose.

Je ne suis donc pas sûr que le «S» soit toujours une bonne idée.

Michel
la source