Devons-nous éviter d'utiliser des modèles de conception dans des projets en constante évolution?

32

Un de mes amis travaille pour une petite entreprise sur un projet que chaque développeur détesterait: il est poussé à publier le plus rapidement possible, il est le seul qui semble se soucier de la dette technique, le client n'a pas de formation technique, etc.

Il m'a raconté une histoire qui m'a fait réfléchir à la pertinence des modèles de conception dans des projets comme celui-ci. Voici l'histoire.

Nous devions afficher les produits à différents endroits sur le site. Par exemple, les gestionnaires de contenu peuvent afficher les produits, mais également les utilisateurs finaux ou les partenaires via l'API.

Parfois, des informations manquaient dans les produits: par exemple, un groupe d’entre eux n’avait aucun prix lors de la création du produit, mais le prix n’était pas encore précisé. Certains n'avaient pas de description (la description étant un objet complexe avec des historiques de modifications, un contenu localisé, etc.). Certains manquaient d'informations sur l'envoi.

Inspiré par mes lectures récentes sur les modèles de conception, j'ai pensé qu'il s'agissait d'une excellente occasion d'utiliser le modèle d'objet magique . Alors je l'ai fait et tout était lisse et propre. Il suffisait d'appeler product.Price.ToString("c")pour afficher le prix ou product.Description.Currentpour afficher la description; aucun truc conditionnel requis. Jusqu'au jour où l'intervenant a demandé à l'afficher différemment dans l'API, en disposant d'un nullfichier JSON. Et aussi différemment pour les gestionnaires de contenu en affichant "Prix non spécifié [changement]". Et j'ai dû assassiner mon motif Null Object bien-aimé, car il n'était plus nécessaire.

De la même manière, j'ai dû supprimer quelques usines abstraites et quelques constructeurs. J'ai fini par remplacer mon magnifique motif Facade par des appels directs et laids, car les interfaces sous-jacentes changeaient deux fois par jour pendant trois mois, et même le Singleton me quittait. lorsque les exigences indiquent que l'objet concerné doit être différent en fonction du contexte.

Plus de trois semaines de travail ont consisté à ajouter des motifs, puis à les déchirer un mois plus tard. Mon code est finalement devenu suffisamment spaghetti pour que personne, y compris moi, ne puisse le conserver. Ne vaudrait-il pas mieux ne jamais utiliser ces schémas?

En effet, je devais travailler moi-même sur ce type de projets où les exigences changent constamment et sont dictées par des personnes qui ne tiennent pas vraiment compte de la cohésion ou de la cohérence du produit. Dans ce contexte, votre agilité importe peu, vous apportez une solution élégante à un problème, et lorsque vous le mettez finalement en œuvre, vous découvrez que les exigences ont changé si radicalement que votre solution élégante ne convient pas. plus longtemps.

Quelle serait la solution dans ce cas?

  • Ne pas utiliser de modèles de conception, arrêter de penser et écrire du code directement?

    Il serait intéressant de réaliser une expérience dans laquelle une équipe écrit du code directement, tandis qu'une autre réfléchit à deux fois avant de taper, prenant le risque de jeter le design original quelques jours plus tard: qui sait, peut-être que les deux équipes auraient le même dette technique. En l'absence de telles données, j'affirmerais simplement qu'il ne semble pas correct de taper du code sans réfléchir au préalable lorsque l'on travaille sur un projet de 20 hommes-mois.

  • Conservez le modèle de conception qui n’a plus de sens et essayez d’ajouter plus de modèles à la situation nouvellement créée?

    Cela ne semble pas juste non plus. Les modèles sont utilisés pour simplifier la compréhension du code; mettre trop de motifs, et le code deviendra un gâchis.

  • Commencez à penser à un nouveau design qui englobe les nouvelles exigences, puis modifiez lentement l'ancien design pour le transformer en nouveau?

    En tant que théoricien et celui qui favorise Agile, je suis totalement dedans. En pratique, lorsque vous savez que vous devez revenir au tableau blanc toutes les semaines pour refaire la majeure partie du modèle précédent et que le client n'a tout simplement pas les moyens de le payer, ni le temps d'attendre. , cela ne fonctionnera probablement pas.

Alors, des suggestions?

Arseni Mourzenko
la source
43
Je pense que c'est un faux dilemme. De l'aveu même de votre ami, le code est maintenant une plaque de spaghettis intenable. Ce n'est pas la faute des modèles de logiciels; c'est l'incapacité de votre ami à utiliser ces motifs correctement, de manière à augmenter la facilité de maintenance et non à la diminuer. Quand je peux trouver des exemples spécifiques, je posterai une réponse appropriée.
Robert Harvey
5
En outre, FWIW, tout client qui n’a pas une certaine tolérance aux coûts de dérive ne devrait probablement pas utiliser Agile, à moins que des indemnités ne soient prévues dans les coûts liés aux exigences changeantes.
Robert Harvey
5
Je soupçonne que sans les modèles de conception, le code aurait atteint un état incontrôlable beaucoup plus tôt
Steven A. Lowe
28
Cette question n'a aucun sens. Le seul moyen "d'éviter les modèles de conception" est de ne pas écrire de logiciel du tout. Des éléments tels que "Motif XYZ" ne sont que des noms donnés à des stratégies de codage communes pour permettre aux programmeurs de transmettre plus facilement des informations et des conseils sur la structure de notre code et ses choix. Tout choix de conception dans votre code pourrait recevoir un nom et être appelé "modèle de conception", bien que ce ne soit pas nécessairement un nom largement connu (à moins que, je suppose, vous soyez fier de votre conception unique et suffisamment motivé pour lui attribuer un nom et un blog. ou quelque chose).
Jason C
9
C'est-à-dire que vous pouvez éviter d'appeler votre pied un "pied" mais vous avez toujours ce pied, il est juste plus difficile d'en parler avec quelqu'un si vous ne l'appelez pas un "pied". Vous pensez peut-être que vous "évitez les modèles de conception", mais si vous proposez une conception décente qui fonctionne, puis prenez du recul et jetez un coup d'oeil, vous constaterez probablement que votre conception vient finalement de s'intégrer à l'un des modèles nommés courants, si vous l'appelez comme ça ou pas.
Jason C

Réponses:

86

Je vois quelques fausses hypothèses dans cette question:

  • Bien que appliqué correctement, le code avec des modèles de conception nécessite plus de temps que le code sans ces modèles.

Les modèles de conception ne sont pas une fin en soi, ils devraient vous servir et non l'inverse. Si un modèle de conception ne rend pas le code plus facile à mettre en œuvre, ou du moins à être mieux évolutif (cela signifie: plus facile à adapter à des exigences changeantes), alors le modèle manque son but. N'appliquez pas de motifs lorsqu'ils ne facilitent pas la "vie" de l'équipe. Si le nouveau modèle d'objet Null servait votre ami depuis le temps où il l'utilisait, tout irait bien. Si cela devait être éliminé plus tard, cela pourrait être aussi correct. Si le modèle d'objet Null ralentissait la mise en œuvre (correcte), son utilisation était incorrecte. Notez, de cette partie de l'histoire, on ne peut conclure aucune cause de "code spaghetti" jusqu'à présent.

  • le client est à blâmer parce qu'il n'a pas de formation technique et ne se soucie pas de la cohésion ou de la cohérence du produit

Ce n'est ni son travail ni sa faute! Votre travail consiste à veiller à la cohésion et à la cohérence. Lorsque les exigences changent deux fois par jour, votre solution ne devrait pas consister à sacrifier la qualité du code. Dites simplement au client combien de temps cela prend et si vous pensez que vous avez besoin de plus de temps pour concevoir le projet "correctement", ajoutez une marge de sécurité suffisante à toute estimation. Surtout lorsque vous avez un client qui essaie de faire pression sur vous, utilisez le "principe de Scotty" . Et quand vous vous disputez avec un client non technique au sujet de l'effort, évitez les termes tels que "refactoring", "tests unitaires", "modèles de conception" ou "documentation de code" - des choses qu'il ne comprend pas et qu'il considère probablement comme "inutiles". un non-sens "parce qu'il n'y voit aucune valeur. ou du moins compréhensible pour le client (fonctionnalités, sous-fonctionnalités, changements de comportement, documents utilisateur, corrections d'erreur, optimisation des performances, etc.).

  • la solution aux besoins changeants est de changer rapidement le code

Honnêtement, si "les interfaces sous-jacentes changent deux fois par jour pendant trois mois", la solution ne devrait pas être de réagir en modifiant le code deux fois par jour. La vraie solution consiste à demander pourquoi les exigences changent si souvent et s'il est possible de faire un changement à cette étape du processus. Peut-être qu'une analyse plus en amont aidera. Peut-être que l'interface est trop large parce que la limite entre les composants est mal choisie. Parfois, il est utile de demander plus d’informations sur la partie des exigences qui sont stables et celles qui sont encore en cours de discussion (et reporte effectivement la mise en œuvre des éléments en discussion). Et parfois, certaines personnes doivent simplement être "frappées dans le cul" pour ne pas changer d'avis deux fois par jour.

Doc Brown
la source
30
+1 - Vous ne pouvez pas résoudre les problèmes de personnes avec des solutions techniques.
Telastyn
1
+1, mais "ou au moins mieux évolutif (cela signifie: plus facile à adapter aux exigences changeantes)" - Je qualifierais cela avec des exigences raisonnablement changeantes , non?
Fuhrmanator
1
@ Fuhrmanator: Je pense que c'est difficile à discuter en termes généraux. IMHO, il est évident qu'aucun modèle de conception ne vous aidera lorsque votre première exigence est "nous avons besoin d'un traitement de texte" et que cela devient "nous avons besoin d'un simulateur de vol". Pour des changements d’exigences moins radicaux, il n’est pas toujours facile de décider de ce qui vous aidera à faire évoluer votre logiciel. La meilleure chose à faire à mon humble avis est de ne pas appliquer trop de motifs, mais quelques principes - principalement les principes SOLID et YAGNI. Et je suis tout à fait d’accord avec Telastyn. Lorsque les exigences changent trop souvent, ce n’est probablement pas un problème technique.
Doc Brown
4
+1 - "Honnêtement, si" les interfaces sous-jacentes changent deux fois par jour pendant trois mois ", la solution ne devrait pas consister à réagir en modifiant le code deux fois par jour. La vraie solution consiste à demander pourquoi les exigences changent si souvent et si il est possible d’apporter un changement à cette étape du processus. "Si l’on vous donne constamment de nouvelles orientations, il est préférable de s’asseoir avec toutes les parties prenantes et de réaffirmer les attentes. Résolvez vos différends et espérons ne pas gaspiller temps et argent pour tout le monde en donnant au projet un objectif plus clair.
Krillgar
1
@Cornelius Doc Brown a dit que c'était difficile sans concret. Les exigences allant d'un traitement de texte à un simulateur de vol ne seraient pas raisonnables; aucun modèle de conception ne serait utile. Il y a beaucoup de zones grises entre son exemple et, disons, l'ajout d'un nouveau format de fichier à la fonction Enregistrer du traitement de texte (ce qui est très raisonnable). Sans détails, il est difficile de discuter. En outre, ce n'est pas que l' on ne serait pas envie de changer. C’est que de tels changements sont difficiles, si vous avez déjà fait un choix de conception en fonction d’une exigence. Le traitement de texte par rapport à simulateur de vol est un excellent exemple de choix précoce.
Fuhrmanator
43

Mon humble avis est qu'il ne faut pas éviter ou éviter d'utiliser des motifs.

Les modèles de conception sont simplement des solutions bien connues et fiables aux problèmes généraux, qui ont reçu un nom. Ils ne sont pas différents sur le plan technique de toutes les autres solutions ou conceptions imaginables.

Je pense que le problème réside peut-être dans le fait que votre ami pense "appliquer ou non un modèle de conception", au lieu de "quelle est la meilleure solution à laquelle je puisse penser, y compris, mais sans s'y limiter, les modèles Je sais".

Peut-être que cette approche l’amène à utiliser des modèles de manière partiellement artificielle ou forcée, dans des endroits où ils n’appartiennent pas. Et c'est ce qui résulte en désordre.

Aviv Cohn
la source
13
+1 "Les modèles de conception sont tout simplement des solutions bien connues et de confiance aux problèmes généraux, qui ont reçu un nom. Ils ne diffèrent pas techniquement de toute autre solution ou conception à laquelle vous pouvez penser." Exactement. Les gens sont tellement absorbés par les modèles de conception nommés qu'ils oublient que ceux-ci ne sont rien de plus que des noms donnés à diverses stratégies afin de faciliter la communication entre nous, programmeurs, au sujet de notre code et de nos choix de conception. Cette attitude est très souvent associée à la tentative d'imposer des "schémas" inappropriés à des problèmes qui ne sont pas nécessairement avantageux - un grand désastre.
Jason C
14

Dans votre exemple d'utilisation du modèle Null Object, je crois qu'il a finalement échoué car il répondait aux besoins du programmeur et non à ceux du client. Le client devait afficher le prix sous une forme appropriée au contexte. Le programmeur avait besoin de simplifier une partie du code d'affichage.

Ainsi, lorsqu'un modèle de conception ne répond pas aux exigences, dit-on que tous les modèles de conception sont une perte de temps ou disons-nous qu'il nous faut un modèle de conception différent?

BobDalgleish
la source
9

Il semblerait que l’erreur était davantage de supprimer les objets de modèle que de les utiliser. Dans la conception initiale, l'objet null semble avoir fourni une solution à un problème. Cela n'a peut-être pas été la meilleure solution.

Être la seule personne à travailler sur un projet vous donne l'occasion de faire l'expérience de tout le processus de développement. Le gros inconvénient est de ne pas avoir quelqu'un pour être votre mentor. Prendre le temps d'apprendre et d'appliquer les meilleures ou les meilleures pratiques est susceptible de porter ses fruits rapidement. L'astuce consiste à identifier quelle pratique apprendre quand.

Le chaînage des références sous la forme product.Price.toString ('c') constitue une violation de la loi de Demeter . J'ai vu toutes sortes de problèmes avec cette pratique, dont beaucoup sont liés à des valeurs nulles. Une méthode telle que product.displayPrice ('c') pourrait gérer les prix nuls en interne. De même, product.Description.Current pourrait être géré par product.displayDescription (), product.displayCurrentDescription (). ou product.diplay ('Actuel').

La gestion de la nouvelle exigence pour les gestionnaires et les fournisseurs de contenu doit être gérée en tenant compte du contexte. Il existe une variété d'approches qui peuvent être utilisées. Les méthodes d'usine peuvent utiliser différentes classes de produits en fonction de la classe d'utilisateurs à laquelle elles seront affichées. Une autre approche consisterait pour les méthodes d’affichage de classe de produit à construire différentes données pour différents utilisateurs.

La bonne nouvelle est que vous réalisez que les choses deviennent incontrôlables. J'espère qu'il a le code en contrôle de révision. Cela lui permettra d’annuler les mauvaises décisions qu’il prendra invariablement. Une partie de l'apprentissage consiste à essayer différentes approches, dont certaines vont échouer. S'il peut gérer les prochains mois, il trouvera peut-être des solutions qui lui simplifieront la vie et permettront de nettoyer les spaghettis. Il pourrait essayer de réparer une chose chaque semaine.

BillThor
la source
2
Cela pourrait également indiquer, puisque vous "devez" enfreindre la loi de Demeter, que le modèle utilisé à la surface était mal adapté. Pourquoi le "modèle de vue" (utilisé dans un sens vague) n'a-t-il pas seulement la description à afficher? (Par exemple, pourquoi existe-t-il plus que la descendance actuelle au niveau de l'interface utilisateur?). La couche de gestion peut préparer un objet correctement rempli dans la couche de l'interface utilisateur qui a déjà un contenu différent selon que c'est le responsable ou non.
Cornelius
7

La question semble être fausse à beaucoup de points. Mais les plus flagrants sont:

  • Pour le modèle d'objet Null que vous avez mentionné, une fois les exigences modifiées, vous modifiez un peu le code. C'est bien, mais cela ne signifie pas que vous «assassinez» le modèle d'objet nul (au fait, soyez prudent dans votre formulation, cela semble trop extrême, certaines personnes aussi paranoïaques ne verront pas cela comme drôle du tout).

Beaucoup de gens ont dit, à juste titre, que les modèles de conception reposent essentiellement sur l’étiquetage et la désignation d’une pratique courante. Alors pensez à une chemise, une chemise a un col, pour quelque raison que ce soit, vous enlevez le col ou une partie du col. La dénomination et l'étiquetage changent, mais il s'agit toujours d'une chemise. C'est exactement le cas ici, des changements mineurs dans les détails ne signifiant pas que vous avez "assassiné" ce schéma. (encore une fois l'esprit la formulation extrême)

  • La conception dont vous avez parlé est mauvaise, car lorsque les modifications apportées aux exigences deviennent des modifications mineures, vous apportez des modifications de conception stratégiques massives partout. À moins que le problème commercial de haut niveau ne soit modifié, vous ne pouvez pas justifier de modifications massives de la conception.

D'après mon expérience, lorsque des exigences mineures apparaissent, il vous suffit de modifier une petite partie de la base de code. Certains peuvent être un peu hacky, mais rien de trop grave pour affecter de manière substantielle la maintenabilité ou la lisibilité et souvent quelques lignes de commentaires pour expliquer la partie hacky suffiront. C'est aussi une pratique très courante.

InforméA
la source
7

Arrêtons-nous un instant pour examiner la question fondamentale: créer un système dans lequel le modèle d'architecture est trop couplé à des fonctionnalités de bas niveau dans le système, ce qui provoque une rupture fréquente de l'architecture dans le processus de développement.

Je pense que nous devons nous rappeler que l'utilisation de l'architecture et des modèles de conception qui s'y rapportent doit être posée à un niveau approprié, et que l'analyse du niveau approprié n'est pas triviale. D'une part, vous pouvez facilement maintenir l'architecture de votre système à un niveau trop élevé avec uniquement des contraintes très élémentaires telles que "MVC", ce qui peut conduire à des occasions manquées, telles que des directives claires et un effet de levier du code, et à un code spaghetti facilement. s'épanouir dans tout cet espace libre.

D’autre part, vous pourriez aussi bien sur-architecturer votre système, par exemple en fixant les contraintes à un niveau détaillé, en supposant que vous puissiez compter sur des contraintes qui sont en réalité plus volatiles que ce à quoi vous vous attendiez. vous obliger à constamment remodeler et reconstruire, jusqu'à ce que vous commencez à désespérer.

Les modifications apportées aux exigences d’un système seront toujours présentes, dans une mesure plus ou moins grande. Et les avantages potentiels de l’utilisation de l’architecture et des modèles de conception seront toujours présents. Il n’est donc pas vraiment question d’utiliser des modèles de conception ou non, mais à quel niveau vous devriez les utiliser.

Cela nécessite non seulement de comprendre les exigences actuelles du système proposé, mais également d'identifier quels en sont les aspects qui peuvent être vus comme des propriétés de base stables du système et quelles propriétés sont susceptibles de changer au cours du développement.

Si vous constatez que vous devez constamment vous battre avec du code spaghetti non organisé, vous ne faites probablement pas assez d’architecture, ni à un niveau élevé. Si vous constatez une rupture fréquente de votre architecture, vous utilisez probablement une architecture trop détaillée.

L’utilisation de l’architecture et des modèles de design n’est pas une chose dans laquelle vous pouvez simplement «enduire» un système, comme si vous peigniez un bureau. Ce sont des techniques qui doivent être appliquées judicieusement, à un niveau où les contraintes sur lesquelles vous devez compter ont de fortes chances d’être stables, et où ces techniques valent vraiment la peine de modéliser l’architecture et de mettre en œuvre les contraintes / architecture / modèles actuels. comme code.

En ce qui concerne la question d’une architecture trop détaillée, vous pouvez également déployer beaucoup d’efforts dans le domaine de l’architecture où elle n’apporte pas beaucoup de valeur. Voir l’architecture axée sur les risques pour référence, j’aime ce livre - Juste assez d’architecture logicielle , peut-être que vous aussi.

modifier

Clarifié ma réponse car je réalisais que je m'exprimais souvent comme "trop ​​d'architecture", où je voulais vraiment dire "architecture trop détaillée", ce qui n'est pas exactement la même chose. Une architecture trop détaillée peut probablement être souvent considérée comme une architecture "trop", mais même si vous maintenez l’architecture à un bon niveau et créez le plus beau système que l’humanité ait jamais vu, il se peut que cela demande trop d’efforts en architecture si les priorités sont les mêmes. sur les fonctionnalités et les délais de commercialisation.

Alex
la source
+1 Ce sont de très bonnes réflexions sur le très haut niveau. Je pense que vous devez regarder dans un système, mais comme vous l'avez dit, cela nécessite beaucoup d'expérience dans la conception de logiciels.
Samuel
4

Votre ami semble faire face à de nombreux vents contraires basés sur son anecdote. C'est malheureux et cela peut être un environnement très difficile. En dépit de la difficulté, il était sur la bonne voie pour utiliser des modèles afin de lui faciliter la vie, et il est dommage qu'il l'ait quitté. Le code spaghetti est le résultat final.

Comme il existe deux problèmes différents, technique et interpersonnel, je traiterai chacun séparément.

Interpersonnel

Votre ami a du mal à s'adapter à l'évolution rapide des exigences et à son impact sur sa capacité à écrire du code maintenable. Je dirais d’abord que les exigences qui changent deux fois par jour, chaque jour pendant une aussi longue période constituent un problème plus grave et suscitent des attentes implicites irréalistes. Les exigences changent plus rapidement que le code ne peut changer. Nous ne pouvons pas nous attendre à ce que le code, ou le programmeur, continue. Ce rythme rapide de changement est symptomatique d'une conception incomplète du produit souhaité à un niveau supérieur. C'est un problème. S'ils ne savent pas ce qu'ils veulent vraiment, ils vont perdre beaucoup de temps et d'argent pour ne jamais l'obtenir.

Il serait peut-être bon de définir des limites pour les changements. Regroupez les modifications dans des ensembles toutes les deux semaines, puis congelez-les pendant les deux semaines de leur mise en œuvre. Construisez une nouvelle liste pour les deux prochaines semaines. J'ai le sentiment que certains de ces changements se chevauchent ou se contredisent (par exemple, un va-et-vient entre deux options). Lorsque les changements surviennent rapidement et furieusement, ils ont tous la priorité. Si vous les laissez accumuler dans une liste, vous pouvez travailler avec eux pour organiser et hiérarchiser ce qui est le plus important pour maximiser les efforts et la productivité. Ils verront peut-être que certains de leurs changements sont stupides ou moins importants, donnant ainsi à votre ami un peu de marge de manœuvre.

Ces problèmes ne devraient cependant pas vous empêcher d'écrire un bon code. Mauvais code conduit à des problèmes plus graves. Il faudra peut-être du temps pour passer d’une solution à l’autre, mais le fait même que c’est possible montre les avantages des bonnes pratiques de codage par le biais de modèles et de principes.

Dans un environnement de changements fréquents, la dette technique sera due à un moment donné. Il est de loin préférable de payer pour cela plutôt que de jeter l'éponge et d'attendre que cela devienne trop gros pour être surmonté. Si un motif n'est plus utile, modifiez-le, mais ne revenez pas aux méthodes de codage des cow-boys.

Technique

Votre ami semble bien comprendre les modèles de conception de base. L'objet nul est une bonne approche du problème auquel il était confronté. En vérité, c'est toujours une bonne approche. Il semble avoir du mal à comprendre les principes qui sous-tendent les modèles, le pourquoi de ce qu’ils sont. Sinon, je ne crois pas qu'il aurait abandonné son approche.

(Ce qui suit est un ensemble de solutions techniques qui n’avaient pas été demandées dans la question initiale, mais qui montrent comment nous pourrions adhérer à des modèles à des fins d’illustration.)

Le principe de l'objet null est l'idée d' encapsuler ce qui varie . Nous cachons ce qui change pour ne pas avoir à le gérer partout ailleurs. Ici, l'objet nul encapsulait la variance dans l' product.Priceinstance (je l'appellerai un Priceobjet et un prix d'objet nul le sera NullPrice). Priceest un objet de domaine, un concept d'entreprise. Parfois, dans notre logique métier, nous ne connaissons pas encore le prix. Ça arrive. Cas d'utilisation parfait pour l'objet null. Prices ont une ToStringméthode qui affiche le prix, ou une chaîne vide si elle n’est pas connue (ou NullPrice#ToStringrenvoie une chaîne vide). C'est un comportement raisonnable. Ensuite, les exigences changent.

Nous devons générer un nullaffichage dans la vue API ou une chaîne différente dans la vue des gestionnaires. Comment cela affecte-t-il notre logique métier? Eh bien, ça ne va pas. Dans la déclaration ci-dessus, j'ai utilisé le mot "view" deux fois. Ce mot n'a probablement pas été prononcé explicitement, mais nous devons nous entraîner à entendre les mots cachés dans les exigences. Alors, pourquoi la vue a-t-elle tant d'importance? Parce que cela nous indique où le changement doit réellement se produire: à notre avis.

À part : que nous utilisions ou non un framework MVC n'est pas pertinent ici. Alors que MVC a un sens très spécifique pour «View», je l’utilise dans le sens plus général (et peut-être plus applicable) d’un code de présentation.

Nous avons donc vraiment besoin de résoudre ce problème dans la vue. Comment pourrions-nous faire cela? Le moyen le plus simple de le faire serait de faire une ifdéclaration. Je sais que l'objet null avait pour but de supprimer tous les ifs, mais nous devons être pragmatiques. Nous pouvons vérifier la chaîne pour voir si elle est vide et passer:

if(product.Price.ToString("c").Length == 0) { // one way of many
    writer.write("Price unspecified [Change]");
} else {
    writer.write(product.Price.ToString("c"));
}

Comment cela affecte-t-il l'encapsulation? La partie la plus importante ici est que la logique de vue est encapsulée dans la vue . De cette manière, nous pouvons complètement isoler nos objets de logique métier / domaine des modifications apportées à la logique de vue. C'est moche, mais ça marche. Ce n'est pas la seule option, cependant.

Nous pourrions dire que notre logique commerciale a légèrement changé en ce sens que nous voulons générer des chaînes par défaut si aucun prix n'est défini. Nous pouvons modifier légèrement notre Price#ToStringméthode (créer une méthode surchargée). Nous pouvons accepter une valeur de retour par défaut et renvoyer que si aucun prix n'est défini:

class Price {
    ...
    // A new ToString method
    public string ToString(string c, string default) {
        return ToString(c);
    }
    ...
}

class NullPrice {
    ...
    // A new ToString method
    public string ToString(string c, string default) {
        return default;
    }
    ...
}

Et maintenant notre code de vue devient:

writer.write(product.Price.ToString("c", "Price unspecified [Change]"));

Le conditionnel est parti. Cependant, en faire trop, des méthodes de cas particuliers pourraient proliférer dans vos objets de domaine. Cela n'a donc de sens que s'il n'y a que quelques cas.

Nous pourrions plutôt créer une IsSetméthode sur Pricelaquelle retourne un booléen:

class Price {
    ...
    public bool IsSet() {
        return return true;
    }
    ...
}

class NullPrice {
    ...
    public bool IsSet() {
        return false;
    }
    ...
}

Voir la logique:

if(product.Price.IsSet()) {
    writer.write(product.Price.ToString("c"));
} else {
    writer.write("Price unspecified [Change]");
}

Nous voyons le retour du conditionnel dans la vue, mais la logique de gestion indique plus clairement si le prix est défini. Nous pouvons utiliser Price#IsSetailleurs maintenant que nous l'avons disponible.

Enfin, nous pouvons résumer l’idée de présenter un prix entièrement dans une aide pour la vue. Cela masquerait le conditionnel, tout en préservant l'objet de domaine autant que nous le voudrions:

class PriceStringHelper {
    public PriceStringHelper() {}

    public string PriceToString(Price price, string default) {
        if(price.IsSet()) { // or use string length to not change the Price class at all
           return price.ToString("c");
        } else {
            return default;
        }
    }
}

Voir la logique:

writer.write(new PriceStringHelper().PriceToString(product.Price, "Price unspecified [Change]"));

Il y a beaucoup plus de façons de faire les changements (on pourrait généraliser PriceStringHelperdans un objet qui retourne un défaut si une chaîne est vide), mais ce sont quelques rapides qui préservent (pour la plupart) les modèles et les principes, comme ainsi que l'aspect pragmatique de ce changement.

cbojar
la source
3

La complexité d'un motif de conception peut vous déranger si le problème qu'il était censé résoudre disparaît soudainement. Malheureusement, en raison de l’enthousiasme et de la popularité des modèles de conception, ce risque est rarement expliqué. L'anecdote de votre ami aide beaucoup à montrer que les modèles ne rapportent rien. Jeff Atwood a quelques mots de choix sur le sujet.

Documentez les points de variation (ce sont des risques) dans les exigences

Un grand nombre des modèles de conception les plus complexes (l'objet Null, pas tellement) contiennent le concept de variations protégées , c'est-à-dire "Identifiez les points de variation ou d'instabilité prédits; assignez des responsabilités pour créer une interface stable autour d'eux". Adaptateur, visiteur, façade, couches, observateur, stratégie, décorateur, etc. exploitent tous ce principe. Ils "payent" lorsque le logiciel doit être étendu dans la dimension de la variabilité attendue et que les hypothèses "stables" restent stables.

Si vos exigences sont si instables que vos "variations prévues" sont toujours fausses, les schémas que vous appliquez vous causeront de la douleur ou seront au mieux inutilement complexes.

Craig Larman parle de deux possibilités d'appliquer des variantes protégées:

  • points de variation - dans le système actuel, les exigences actuelles, telles que plusieurs interfaces devant être prises en charge, et
  • points d'évolution - points de variation spéculatifs qui ne sont pas présents dans les exigences existantes.

Les deux sont supposés être documentés par les développeurs, mais vous devriez probablement avoir l'engagement du client envers les points de variation.

Pour gérer les risques, vous pouvez dire que tout modèle de conception appliquant la PV doit être lié à un point de variation des exigences signées par le client. Si un client modifie un point de variation dans les exigences, votre conception devra peut-être être radicalement modifiée (car vous avez probablement investi dans la conception pour prendre en charge cette variation). Pas besoin d'expliquer la cohésion, le couplage, etc.

Par exemple, votre client souhaite que le logiciel fonctionne avec trois systèmes de stockage hérités différents. C'est un point de variation que vous concevez. Si le client abandonne cette exigence, vous disposez bien entendu d'une infrastructure de conception inutile. Le client doit savoir que les points de variation coûtent quelque chose.

CONSTANTS dans le code source sont une forme simple de PV

Une autre analogie avec votre question serait de demander si l’utilisation CONSTANTSde code source est une bonne idée. En se référant à cet exemple , supposons que le client ait perdu le besoin de mots de passe. Ainsi, la MAX_PASSWORD_SIZEpropagation constante dans votre code deviendrait inutile et constituerait même un obstacle à la maintenance et à la lisibilité. Souhaitez-vous blâmer l'utilisation de CONSTANTSla raison?

Fuhrmanator
la source
2

Je pense que cela dépend au moins en partie de la nature de votre situation.

Vous avez mentionné des exigences en constante évolution. Si le client dit "Je veux que cette application apicole fonctionne aussi avec les guêpes", cela semble être le genre de situation dans laquelle une conception soignée aiderait à progresser et non à l'empêcher (surtout si vous considérez qu'elle voudra peut-être à l'avenir gardez les mouches des fruits aussi.)

D'un autre côté, si la nature du changement ressemble davantage à "Je souhaite que cette application apicole gère la masse salariale de mon conglomérat de laveries automatiques", aucune quantité de code ne vous sortira de votre trou.

Il n'y a rien de fondamentalement bon dans les modèles de conception. Ce sont des outils comme les autres. Nous ne les utilisons que pour faciliter notre travail à moyen et à long terme. Si un outil différent (tel que la communication ou la recherche ) est plus utile, nous l'utilisons.

Benjamin Hodgson
la source