Nous attirons votre attention sur un article dont l'auteur n'approuve pas l'approche purement orientée objet lorsque l'on travaille avec le langage C ++. Nous vous demandons d'évaluer, si possible, non seulement l'argumentation de l'auteur, mais aussi la logique et le style.
Il y a eu beaucoup d' écrits ces derniers temps sur le C ++ et la direction du langage et combien de ce que l'on appelle «C ++ moderne» n'est tout simplement pas une option pour les développeurs de jeux.
Bien que je partage pleinement ce point de vue, j'ai tendance à voir l'évolution du C ++ comme le résultat d'idées omniprésentes enracinées dans lesquelles la plupart des développeurs sont guidés. Dans cet article, je vais essayer d'organiser certaines de ces idées avec mes propres pensées - et peut-être que j'obtiendrai quelque chose de mince.
Programmation orientée objet (POO) en tant qu'outil
Bien que C ++ soit décrit comme un langage de programmation multi-paradigme , dans la pratique, la plupart des programmeurs utilisent le C ++ uniquement comme un langage orienté objet (la programmation générique est utilisée pour «compléter» la POO).
La POO est censée être un outil, l'un des nombreux paradigmes qu'un programmeur peut utiliser pour résoudre des problèmes de code. Cependant, d'après mon expérience, la POO est acceptée par la plupart des professionnels comme la référence en matière de développement logiciel. Fondamentalement, le développement d'une solution commence par déterminer les objets dont nous avons besoin. La solution à un problème spécifique commence après que le code a été distribué entre les objets. Avec la transition vers ce type de pensée orientée objet, la POO passe d'un outil à une boîte à outils entière.
Sur l'entropie comme force secrète qui alimente le développement de logiciels
J'aime penser à une solution POO comme une constellation: c'est un groupe d'objets avec des lignes dessinées au hasard entre eux. Une telle solution peut aussi être considérée comme un graphe dans lequel les objets sont des nœuds, et les relations entre eux sont des arêtes, mais le phénomène de groupe / cluster, qui est véhiculé par la métaphore de la constellation, est plus proche de moi (par rapport à lui, le graphe est trop abstrait).
Mais je n'aime pas la façon dont ces «constellations d'objets» sont composées. Selon moi, chacune de ces constellations n'est rien de plus qu'un instantané de l'image qui s'est formée dans la tête du programmeur et reflète à quoi ressemble l'espace de solution à un moment donné. Même en tenant compte de toutes les promesses faites dans la conception orientée objet sur l'extensibilité, la réutilisabilité, l'encapsulation, etc., l'avenir est imprévisible, nous pouvons donc dans chaque cas offrir une solution exactement au problème auquel nous sommes confrontés actuellement.
Nous devrions être encouragés que nous résolvions «juste» le problème qui est directement devant nous, mais d'après mon expérience, un programmeur utilisant des principes de conception dans l'esprit de la POO crée une solution, tout en se contraignant en supposant que le problème lui-même ne changera pas de manière significative et , par conséquent, la solution peut être considérée comme permanente. Je veux dire qu'à partir de maintenant, les gens commencent à réfléchir à la solution en termes d'objets qui composent la constellation susmentionnée, et non en termes de données et d'algorithmes; le problème lui-même est abstrait.
Néanmoins, le programme est sujet à l'entropie pas moins que tout autre système et, par conséquent, nous savons tous que le code va changer. De plus, de manière imprévisible. Mais pour moi dans ce cas, il est absolument clair que le code se dégradera dans tous les cas, glissant dans le chaos et le désordre, si vous ne le combattez pas consciemment.
J'ai vu ce manifeste de différentes manières dans les solutions POO:
- De nouveaux niveaux intermédiaires apparaissent dans la hiérarchie, alors qu'ils n'étaient pas initialement destinés à être introduits.
- De nouvelles fonctions virtuelles sont ajoutées avec des implémentations vides dans la plupart de la hiérarchie.
- L'un des objets de la constellation nécessite plus de traitement que prévu, ce qui fait que les connexions entre les autres objets commencent à glisser.
- , , , .
- .…
Ce sont tous des exemples d'extensibilité mal organisée. De plus, le résultat est toujours le même, il peut arriver dans quelques mois, ou peut-être dans quelques années. Avec l'aide de la refactorisation, ils essaient d'éliminer les violations des principes de conception de la POO, commises lorsque de nouveaux objets ont été ajoutés à la constellation, et ils ont été ajoutés en raison de la reformulation du problème lui-même. Parfois, la refactorisation aide. Pour un moment. L'entropie est stable et les programmeurs n'ont pas le temps de refactoriser chaque constellation de POO afin de la surmonter, de sorte que tout projet se retrouve régulièrement dans la même situation, dont le nom est le chaos.
Dans le cycle de vie de tout projet POO, il arrive tôt ou tard un point après lequel il est impossible de le maintenir. En règle générale, à ce stade, l'une des deux actions suivantes doit être entreprise:
- « »: - . , , , , , .
- : -, , , .
Remarque: l'option avec une boîte noire nécessitera toujours une réécriture au cas où le développement de nouvelles fonctionnalités doit continuer et / ou la nécessité d'éliminer les bogues subsiste.
La situation de réécriture d'une solution nous ramène au phénomène d'un instantané de l'espace de solution disponible à un moment donné. Alors, qu'est-ce qui a changé entre la conception POO n ° 1 et la situation actuelle? En gros, c'est tout. Le problème a changé, par conséquent, une solution différente est nécessaire.
Pendant que nous écrivions la solution en suivant les principes de la conception de la POO, nous avons résumé le problème, et dès qu'il a changé, notre solution s'est effondrée comme un château de cartes.
Je pense que c'est à ce moment que nous commençons à nous demander ce qui n'a pas fonctionné, nous essayons d'aller dans l'autre sens et de mettre à jour les stratégies de résolution du problème en fonction des résultats du post-mortem (débriefing). Cependant, à chaque fois que je rencontre un tel scénario de «temps de réécriture», rien ne change: les principes de la POO sont à nouveau utilisés, conformément auxquels un nouvel instantané est implémenté, correspondant à l'état actuel de l'espace problème. Le cycle entier est répété.
La facilité de suppression du code comme principe de conception
Dans tout système construit sur le principe de la POO, ce sont les objets de la «constellation» qui reçoivent la plus grande attention. Mais je crois que les relations entre les objets sont aussi importantes, sinon plus, que les objets eux-mêmes.
Je préfère des solutions simples dans lesquelles le graphe de dépendance du code se compose du nombre minimum de nœuds et d'arêtes. Plus la solution est simple, plus il est facile non seulement de la modifier, mais aussi de la supprimer. J'ai également constaté que plus il était facile de supprimer le code, plus vite vous pouviez recentrer la solution et l'adapter à l'évolution des conditions du problème. Dans le même temps, le code devient plus résistant à l'entropie, car il faut beaucoup moins d'efforts pour le maintenir en ordre et l'empêcher de sombrer dans le chaos.
À propos des performances par définition
Mais l'une des principales considérations pour éviter la conception OOP est la performance. Plus vous devez exécuter de code, plus les performances seront mauvaises.
Il est également impossible de ne pas remarquer que les fonctionnalités POO, par définition, ne brillent pas avec les performances. J'ai implémenté une hiérarchie POO simple avec une interface et deux classes dérivées qui remplacent un seul appel de fonction virtuelle pure dans l' Explorateur du compilateur .
Le code de cet exemple imprime «Hello, World!» Ou non, selon le nombre d'arguments transmis au programme. Au lieu de programmer directement tout ce que je viens de décrire, l'un des modèles de conception OOP standard, l'héritage, sera utilisé pour résoudre ce problème dans le code.
Dans ce cas, ce qui est le plus frappant est la quantité de code générée par les compilateurs, même après optimisation. Ensuite, en regardant de plus près, vous pouvez voir à quel point une telle maintenance est coûteuse et en même temps inutile: lorsqu'un nombre d'arguments différent de zéro est passé au programme, le code alloue toujours de la mémoire (appel
new
), charge les adresses des vtable
deux objets, charge l'adresse de la fonction Work()
pour ImplB
et y saute, de sorte qu'ensuite immédiatement retour, car il n'y a rien à faire là-bas. Enfin, il est appelé delete
pour libérer la mémoire allouée.
Aucune de ces opérations n'était nécessaire du tout, mais le processeur les a toutes exécutées correctement.
Ainsi, si l'un des principaux objectifs de votre produit est la réalisation de hautes performances (étrange s'il en serait autrement), alors dans le code, vous devez éviter les opérations coûteuses inutiles, préférant les opérations simples, faciles à juger, et utiliser des constructions qui aident à atteindre cet objectif.
Prenons par exemple Unity . Dans le cadre de leur pratique récente, la performance est l'exactitude en utilisant C #, un langage orienté objet, puisque ce langage est déjà utilisé dans le moteur lui-même. Cependant, ils ont opté pour un sous - ensemble de C # , de plus, sur un qui n'est pas lié de manière rigide à la POO, et sur sa base, ils créent des constructions affûtées pour des performances élevées.
Étant donné que le travail d’un programmeur consiste à résoudre des problèmes à l’aide d’un ordinateur, il est impensable que notre entreprise consacre si peu d’attention à l’écriture de code permettant au processeur de faire le travail pour lequel il est particulièrement doué.
À propos de la lutte contre les stéréotypes
Dans l ' article d' Angelo Pesce «La surconsommation est la racine de tout le mal », l 'auteur frappe la cible (voir dernière section: Les gens) en admettant que la plupart des problèmes logiciels sont en fait des facteurs humains.
Les membres de l'équipe doivent interagir et développer une compréhension commune de l'objectif global et du chemin pour y parvenir. En cas de désaccord au sein de l'équipe, par exemple sur le chemin vers l'objectif, il est nécessaire de développer un consensus pour progresser davantage. Ce n'est généralement pas difficile si les différences d'opinion sont faibles, mais il est beaucoup plus difficile à tolérer si les options diffèrent fondamentalement, dites «POO ou non POO».
Changer d'avis n'est pas facile. Douter de votre point de vue, réaliser à quel point vous vous êtes trompé et ajuster votre cap est difficile et douloureux. Mais il est bien plus difficile de changer d'avis de quelqu'un d'autre!
J'ai eu beaucoup de conversations avec différentes personnes sur la POO et ses problèmes inhérents, et bien que je pense avoir toujours été en mesure d'expliquer pourquoi je pense de cette façon et pas autrement, je ne pense pas avoir réussi à détourner qui que ce soit de la POO.
Certes, au fil des années de travail, j'ai identifié trois principaux arguments pour moi-même, à cause desquels les gens ne sont pas prêts à donner une chance à l'autre côté:
- « ». « ». « » . , , ( , - ). « …».
- « , , , ». «» , , . , « ».
- "Tout le monde connaît la POO, il est très pratique de parler avec des gens dans une langue commune, ayant des connaissances générales." Il s'agit d'une erreur logique appelée «argument au peuple», c'est-à-dire que si presque tous les programmeurs utilisent les principes de la POO, alors cette idée ne peut pas être inappropriée.
Je suis parfaitement conscient que révéler des erreurs logiques dans l'argumentation ne suffit pas à les démystifier. Cependant, je crois qu'en voyant les défauts de vos propres jugements, vous pouvez aller au fond de la vérité et trouver la raison profonde pour laquelle vous rejetez une idée inhabituelle.