J'ai passé beaucoup de temps à lire différents livres sur le "bon design", les "modèles de conception", etc. Je suis un grand fan de l' approche SOLID et chaque fois que j'ai besoin d'écrire un simple morceau de code, je pense à l'avenir. Donc, si implémenter une nouvelle fonctionnalité ou une correction de bogue nécessite simplement l’ajout de trois lignes de code comme ceci:
if(xxx) {
doSomething();
}
Cela ne signifie pas que je vais le faire de cette façon. Si j'ai le sentiment que ce code va probablement devenir plus gros dans un avenir rapproché, je penserai à ajouter des abstractions, à déplacer cette fonctionnalité ailleurs, etc. L'objectif que je poursuis est de garder la complexité moyenne identique à celle d'avant mes modifications.
Je pense que, du point de vue du code, c'est une très bonne idée - mon code n'est jamais assez long et il est assez facile de comprendre les significations de différentes entités, telles que les classes, les méthodes et les relations entre les classes et les objets.
Le problème, c’est que cela prend trop de temps et j’ai souvent l’impression que ce serait mieux si je mettais cette fonctionnalité en œuvre "telle quelle". Il s'agit simplement de "trois lignes de code" par opposition à "nouvelle interface + deux classes pour implémenter cette interface".
Du point de vue du produit (lorsque nous parlons du résultat ), les choses que je fais sont assez absurdes. Je sais que si nous travaillons sur la prochaine version, avoir un bon code est vraiment génial. Mais de l’autre côté, le temps que vous avez consacré à rendre votre code "bon" a peut-être été consacré à la mise en oeuvre de quelques fonctionnalités utiles.
Je me sens souvent très insatisfait de mes résultats - un bon code qui ne peut que faire A est pire qu'un mauvais code qui peut faire A, B, C et D.
Cette approche est-elle susceptible de générer un gain net positif pour un projet de logiciel ou est-ce une perte de temps?
la source
Réponses:
Cela me sent comme une généralité spéculative . Sans savoir (ou du moins être raisonnablement sûr) que vos clients vont avoir besoin des fonctionnalités B, C et D, vous compliquez inutilement votre conception. Un code plus complexe est plus difficile à comprendre et à maintenir à long terme. La complexité supplémentaire n'est justifiée que par des fonctionnalités supplémentaires utiles . Mais nous sommes généralement très mauvais pour prédire l'avenir. La plupart des fonctionnalités que nous pensons peut - être nécessaire à l'avenir ne sera jamais jamais demandé dans la vie réelle.
Alors:
Un bon code qui ne peut que faire A (mais il fait une chose simplement et proprement) est MEILLEUR qu'un mauvais code qui peut faire A, B, C, D (dont certains pourraient être nécessaires dans le futur).
la source
Anecdote time:
J'ai eu deux développeurs qui travaillent pour moi et qui se sont penchés de la sorte vers la sur-ingénierie.
Pour l'un d'entre eux, sa productivité a été paralysée, en particulier lors du démarrage d'un nouveau projet. Surtout si le projet était, par nature, assez simple. En fin de compte, un logiciel qui fonctionne maintenant est ce dont nous avons besoin. Cela a tellement empiré que je devais le laisser partir.
L'autre développeur qui était enclin à une ingénierie excessive l'a compensé par sa très grande productivité. En tant que tel, malgré tout le code étranger, il livrait toujours plus rapidement que la plupart. Cependant, maintenant qu'il est parti, je suis souvent irrité par la quantité de travail supplémentaire nécessaire pour ajouter des fonctionnalités, car vous devez modifier des couches d'abstraction (totalement inutiles), etc.
La morale est donc que la sur-ingénierie prendra du temps supplémentaire qui pourrait être mieux dépensé pour faire des choses utiles. Et pas seulement votre temps, mais aussi de ceux qui doivent travailler avec votre code.
Alors ne le fais pas.
Vous voulez que votre code soit aussi simple que possible (et non plus simple). La gestion de «maybes» ne simplifie pas les choses. Si vous avez tort, vous aurez rendu le code plus complexe sans véritable avantage.
la source
Les principes SOLID et KISS / YAGNI sont des opposés presque polaires. On vous dira que si doSomething () ne peut pas être justifié en tant que partie intégrante du travail que la classe qu’elle appelle est en train de le faire, il devrait appartenir à une classe différente qui est couplée et injectée de manière lâche. L’autre vous dira que si c’est le seul endroit où quelque chose est utilisé, même l’extraire dans une méthode a peut-être été excessif.
C'est ce qui fait que les bons programmeurs valent leur pesant d'or. La structure "appropriée" est une base au cas par cas, nécessitant une connaissance de la base de code actuelle, de la trajectoire future du programme et des besoins de l'entreprise qui le sous-tend.
J'aime suivre cette méthodologie simple en trois étapes.
En gros, voici comment vous mélangez KISS avec SOLID. Lorsque vous écrivez une ligne de code pour la première fois, sachez que ce ne sera qu’une pièce unique; il doit simplement fonctionner et personne ne s'en soucie, alors ne vous en faites pas. La deuxième fois que vous avez placé votre curseur dans cette ligne de code, vous avez réfuté votre hypothèse initiale. en revisitant ce code, vous l’allez probablement l’extraire ou l’accrochant ailleurs. Maintenant, vous devriez nettoyer un peu; extraire des méthodes pour les friandises couramment utilisées, réduire ou éliminer le codage copier / coller, ajouter quelques commentaires, etc. etc. La troisième fois que vous revenez à ce code, il s'agit désormais d'une intersection majeure pour les chemins d'exécution de votre programme. Maintenant, vous devriez le traiter comme une partie essentielle de votre programme et appliquer les règles SOLID dans la mesure du possible.
Exemple: Vous devez écrire une simple ligne de texte sur la console. La première fois que cela se produit, Console.WriteLine () va très bien. Ensuite, vous revenez à ce code après que de nouvelles exigences vous imposent également d'écrire la même ligne dans un journal de sortie. Dans cet exemple simple, il peut ne pas y avoir beaucoup de code "copier / coller" répétitif (ou peut-être qu'il en existe), mais vous pouvez toujours effectuer un nettoyage de base, peut-être extraire une ou deux méthodes pour éviter d'inclure les opérations d'IO dans une logique métier plus profonde . Ensuite, vous revenez lorsque le client souhaite la même sortie texte vers un canal nommé pour un serveur de surveillance. Maintenant, cette routine de sortie est une sorte de gros problème; vous diffusez le même texte de trois manières. Ceci est l'exemple classique d'un algorithme qui tirerait profit d'un motif composite; développer une interface simple IWriter avec une méthode Write (), implémentez cette interface pour créer les classes ConsoleWriter, FileWriter et NamedPipeWriter, puis une fois de plus pour créer une classe composite "MultiWriter", puis exposez une dépendance IWriter sur votre classe, configurez le composite MultiWriter avec les trois rédacteurs réels et connectez-le. Maintenant, c'est assez solide; à partir de maintenant, chaque fois que les conditions requises imposent une sortie nouvelle, il vous suffit de créer un nouvel IWriter et de le brancher sur le MultiWriter, sans avoir à modifier le code de travail existant.
la source
1) Avoir un code qui ne fait que ce qui est supposé faire.
2) Si vous planifiez votre code pour faire A, B, C et D, le client vous demandera finalement E.
Votre code devrait faire ce qui est supposé faire, vous ne devriez pas penser maintenant aux implémentations futures, car vous ne finirez jamais de changer votre code de façon continue, et plus important, vous le sur-concevez. Vous devez refactoriser votre code dès que vous en ressentez le besoin en raison des fonctionnalités actuelles, sans avoir du mal à le préparer à quelque chose qu'il ne fera pas encore, à moins bien sûr que vous l'aviez prévu dans le cadre de l'architecture du projet.
Je vous suggère de lire un bon livre: The Pragmatic Programmer . Cela ouvrira votre esprit et vous apprendra ce que vous devriez faire et ce que vous ne devriez pas faire.
De plus, Code Complete est une excellente ressource qui contient des informations utiles que tous les développeurs (pas seulement les programmeurs) devraient lire.
la source
Peut-être que voici le problème.
Au début, vous n'avez aucune idée de ce que serait le produit final. Ou si vous l'avez, c'est faux. Pour sûr. C'est comme un garçon de 14 ans qui a demandé il y a quelques jours à Programmers.SE s'il devait, pour sa future carrière, choisir entre des applications Web et je ne me souviens plus quoi: il est assez évident que dans quelques années il aimera va changer, il sera intéressé par d'autres domaines, etc.
Si, pour écrire trois lignes de code, vous créez une nouvelle interface et deux classes, vous êtes trop technique. Vous obtiendrez un code difficile à maintenir et à lire , simplement parce que pour chaque ligne de code utile, vous avez deux lignes de code inutiles. Sans compter la documentation XML, les tests unitaires, etc.
Pensez-y: si je veux savoir comment une fonctionnalité est implémentée dans votre code, serait-il plus facile pour moi de lire vingt lignes de code, ou il serait plus rapide et plus facile d'ouvrir des dizaines de classes semi-vides et interfaces pour savoir lequel utilise lequel, comment sont-ils liés, etc.?
Rappelez-vous: une plus grande base de code signifie plus de maintenance. Ne pas écrire plus de code quand vous pouvez l'éviter.
Votre approche est également nuisible d’autres côtés:
Si vous devez supprimer une fonctionnalité, n'est-il pas plus facile de déterminer où une méthode spécifique de vingt lignes est utilisée que de perdre votre temps à comprendre les dépendances entre des dizaines de classes?
Lors du débogage, n’est-il pas plus facile d’avoir une petite trace de pile, ou préférez-vous lire des dizaines de lignes afin de comprendre comment on appelle quoi?
Pour conclure, cela semble similaire à l' optimisation prématurée . Vous essayez de résoudre le problème sans même vous assurer qu'il existe un problème et comment il se trouve. Lorsque vous travaillez sur la version 1 de votre produit, implémentez les fonctionnalités que vous devez implémenter dès maintenant; ne pensez pas à quelque chose que vous comptez mettre en œuvre dans deux ans dans la version 14.
la source
Écrire beaucoup de code qui (probablement) ne sera jamais utilisé est un très bon moyen de se voir attribuer un P45 . Vous n'avez pas de boule de cristal et n'avez aucune idée de la direction finale que prendra le développement, alors consacrer du temps à ces fonctions ne coûte que de l'argent sans contrepartie.
la source
Essayer de prédire ce qui pourrait être nécessaire à partir du code dans le futur finit souvent par devenir une sur-ingénierie inutile (une habitude que j'essaie actuellement de secouer). Je dirais juste faire les trois lignes. Lorsque le besoin s'en fait sentir (et pas avant), refactor. De cette façon, votre code fait toujours ce dont il a besoin sans être trop compliqué et développe naturellement une bonne structure grâce au refactoring.
la source
Je dis souvent que le codage est comme le côté clair / sombre de la Force - le côté "clair" nécessite plus d’efforts mais donne de meilleurs résultats. Le côté "obscur" est rapide et facile et procure immédiatement de plus grands avantages, mais il vous corrompt par la suite. Une fois que vous aurez emprunté le chemin sombre, il dominera à jamais votre destin.
Je rencontre ce problème tout le temps, à chaque travail que j'ai jamais eu, c'est comme une malédiction à laquelle je ne peux pas échapper. La culture d'entreprise est toujours la voie du côté obscur, et des solutions rapides pour corriger de nouvelles fonctionnalités, et mes appels au refactoring et à l'écriture de code tombent correctement dans l'oreille d'un sourd, si cela ne conduit pas à mon licenciement " essayer de changer les choses "(sans blague, cela m'est arrivé une poignée de fois, parce que je voulais introduire des modèles de conception et m'éloigner des hacks rapides).
La triste vérité est que souvent, le côté stupide / obscur est la façon dont vous devez marcher, vous devez simplement vous assurer de marcher légèrement. J'ai lentement et malheureusement rendu compte que les programmeurs qui comprennent la bonne façon d'écrire des logiciels, ce qui suit SOLID, en utilisant des modèles de conception, obéissant SoC, etc. sont beaucoup moins fréquents que les hacks désemparés qui écriront une
if
déclaration pour corriger un bug, et quand plus de bugs surviennent, ajoutez simplement cette déclaration au lieu de penser «Peut-être qu'il y a une meilleure façon de faire cela» et refactoriser le code pour qu'il soit correctement extensible.la source
if
est beaucoup plus facile à entretenir queIAbstractBugFixer
d’unIAbstractBugFixerFactory
. Quand, et, SI, vous arrivez à ajouter une secondeif
, alors il est temps de refactoriser. Les modèles de conception et SOLID sont très importants pendant la phase d'architecture, mais pas lorsque le produit est déjà en cours d'exécution et qu'il est écrit dans un style sur lequel tous les membres de l'équipe se sont mis d'accord.Être conscient de ce qui pourrait arriver (futur) n’est JAMAIS mauvais. Penser à ce qui pourrait être une meilleure façon de faire quelque chose fait partie de ce qui vous rend bien dans votre travail. La partie la plus difficile consiste à déterminer si le temps passé: rapport de gain est justifié. Nous avons tous vu des situations où les gens font le "facile si" d'arrêter les saignements immédiats (et / ou crier), et que, lorsque ceux-ci s'additionnent, vous obtenez un code déroutant. Beaucoup d'entre nous ont également fait l'expérience d'une abstraction exagérée qui est un mystère une fois que le codeur original passe à autre chose, ce qui produit également un code confus.
Je voudrais regarder votre situation et poser ces questions:
Ce code est-il essentiel à la mission et sera-t-il beaucoup plus stable si je le re-code? En chirurgie, est-ce que ce refactoring sauve des vies, ou est-il simplement électif et esthétique?
Est-ce que j'envisage de refactoriser le code que nous allons remplacer dans 6 mois?
Suis-je disposé à prendre autant de temps pour documenter la conception et mon raisonnement pour les futurs développeurs que pour passer à la refactorisation?
En ce qui concerne mon design élégant pour l’ajout de nouvelles fonctionnalités, c’est dans le code que les utilisateurs demandent des modifications toutes les semaines ou est-ce la première fois que je le touche cette année?
Il y a des moments où YAGNI et KISS gagnent la journée, mais il y a des jours où un changement fondamental vous permettra de sortir de la spirale descendante qui mène au tristesse. Tant que vous évaluerez de manière réaliste non seulement ce que vous voulez, mais aussi ce que les autres vont devoir faire pour maintenir votre travail, vous serez plus en mesure de déterminer quelle situation est laquelle. Oh, et n'oubliez pas d'écrire ce que vous avez fait et pourquoi. Cela sauvera ceux qui vous suivent, mais aussi vous-même lorsque vous devrez revenir plus tard.
la source
Dans la deuxième édition de Stroustrups 'Le langage de programmation C ++', (je n'ai pas la page disponible), j'ai lu
et j'ai bien suivi les conseils. Bien sûr, il y a des compromis, et vous devez trouver un équilibre, mais de courts fragments sont plus faciles à tester qu'un gros gâchis de spaghettis.
J'ai souvent constaté que, tout en distinguant un cas sur deux, si vous pensez à deux cas, vous ouvrez la porte à de nombreuses possibilités nouvelles, auxquelles vous n'auriez peut-être pas pensé.
Mais vous devez ensuite poser la question YAGNI: est-ce que cela en vaut la peine? Sera-ce vraiment utile? Etre expérimenté signifie que vous aurez rarement tort et en tant que débutant plus souvent tort.
Vous devez être suffisamment critique pour reconnaître un motif et détecter si votre code est difficile à maintenir, en raison d'une trop grande abstraction, ou difficile à maintenir, car tout est résolu à la place.
La solution n'est pas ceci ou cela, mais d'y penser. :)
la source
"un bon code qui ne peut que faire A est pire qu'un mauvais code qui peut faire A, B, C et D."
Cela peut avoir un sens dans le développement de produits; Mais la plupart des professionnels de l'informatique travaillent dans des «projets» plutôt que dans le développement de produits.
Dans les «projets informatiques», si vous programmez un bon composant, celui-ci fonctionnera sans à-coups pendant toute la durée de vie du projet. Il ne pourra pas durer plus de 5 ou 10 ans à compter de ce moment. Le scénario de gestion pourrait être devenu obsolète et un nouveau projet / ERP. le produit aurait pu le remplacer. Pendant cette durée de vie de 5/10 ans, à moins que votre code ne soit défectueux, personne ne remarquera son existence et les mérites de vos meilleures pensées passeront inaperçus! (sauf si vous êtes bon pour jouer fort votre propre trompette!)
Peu de gens ont la possibilité de programmer le 'Windows Ctl + Alt + Del' et ces quelques-uns ont cette chance de ne pas réaliser le potentiel futur de leur code!
la source
De nombreux livres sur le développement Lean et / ou Agile aideront à renforcer cette approche: faites ce qui est nécessaire maintenant. Si vous savez que vous construisez un cadre, ajoutez des abstractions. Sinon, n'ajoutez pas de complexité avant d'en avoir besoin. Je recommande le développement de logiciels Lean , qui introduira de nombreux autres concepts pouvant avoir un impact significatif sur la productivité.
la source
C'est drôle de voir comment les gens parlent de la bonne façon / mauvaise façon de faire les choses. Dans le même temps, la tâche de programmation reste péniblement difficile et ne donne pas de bonnes solutions pour écrire de gros systèmes complexes.
Il se peut qu'un jour nous, les programmeurs, sachions enfin comment écrire des logiciels complexes. Jusque-là, je vous suggère de toujours commencer par une implémentation de prototype "stupide", puis de consacrer juste assez de temps à la refactorisation afin que vos collèges puissent suivre votre code.
la source
Ayant vu de telles conceptions généralisées prématurément qui ne correspondaient pas du tout aux exigences réelles qui sont venues plus tard, j'ai conçu une règle pour moi:
Pour les exigences hypothétiques, écrivez seulement du code hypothétique.
C'est-à-dire qu'il est judicieux de penser aux changements qui pourraient survenir plus tard. Mais n'utilisez ces informations que pour choisir une conception de code pouvant être facilement modifiée et refactorisée si ces exigences sont réellement remplies. Vous pouvez même écrire un code dans votre tête (code hypothétique) que vous voudriez écrire dans ce cas, mais n'écrivez pas de code réel!
la source
Je pense que la mentalité qui vous aidera est de toujours rechercher des solutions concrètes aux problèmes de codage plutôt que des solutions abstraites. Les abstractions ne doivent être ajoutées que lorsqu'elles aident réellement à simplifier la base de code (lorsqu'elles permettent par exemple de réduire la base de code).
De nombreux programmeurs ont constaté que les abstractions émergent presque d'eux-mêmes lorsqu'ils sèchent leur code. Les modèles de conception et les meilleures pratiques vous aident à trouver des occasions de le faire, mais ne constituent pas en soi des objectifs qui valent la peine d'être poursuivis.
la source
Je pense que la sur-ingénierie semble souvent due à l'insécurité liée à l'écriture de code. Tous les principes et modèles abstraits doivent être traités comme des outils pour vous aider. Ce qui arrive souvent, c’est qu’ils sont traités comme des normes auxquelles il faut se conformer.
Je crois qu'un programmeur est toujours mieux placé que son axiome pour décider de l'abstraction.
Le reste a déjà été dit par KeithS
la source
Demandez-vous quels sont les avantages d'un bon design:
Maintenant, demandez-vous si l'ajout de toutes ces couches d'abstractions ajoute vraiment à l'un des points mentionnés ci-dessus. Si ce n'est pas le cas, vous le faites FAUX .
Si vous pouvez ajouter de nouvelles fonctionnalités en ajoutant 3 lignes de code comme ceci:
Alors s'il vous plaît, s'il vous plaît faites-le. Cela indique que votre conception précédente était bonne et facile à adapter. Une fois que vos classes commencent à dépasser un certain seuil, utilisez le refactoring pour scinder les fonctions et éventuellement extraire de nouvelles classes.
Ma règle générale est que les nouvelles fonctionnalités doivent être implémentées de la manière la plus minimaliste possible. Vous pouvez ajouter une conception globale approximative dès que quelque chose est trop gros à saisir (il faut plus d’un jour ou une demi-journée). À partir de là, ajoutez des couches d'abstraction uniquement lorsque la taille du code augmente. Vous refactorez ensuite! Après un certain temps, cela devrait même devenir naturel lorsque vous devez concevoir un morceau un peu plus, ou choisir la route rapide. Une autre indication que certains codes peuvent utiliser un nettoyage est leur réutilisation. Chaque fois que vous ajoutez une nouvelle fonctionnalité ou appelez l'ancien code dans un nouvel emplacement, c'est un bon moment pour examiner l'ancien code et voir si vous pouvez l'améliorer un peu avant d'y ajouter de nouveau. De cette façon, le code chaud deviendra lentement plus propre, tandis que les parties inintéressantes pourrissent lentement et ne vous font pas perdre un temps précieux.
Si vous travaillez comme ça, vous ne pourrez jamais trop concevoir. Il faudra peut-être une certaine discipline pour revenir à l'ancien code lorsque vous souhaitez ajouter quelque chose de nouveau ou pour laisser un nouveau code légèrement plus laid que vous ne le souhaitez, mais vous travaillerez dans le but de créer quelque chose de productif plutôt que trop d'ingénierie.
la source