Si je comprends bien, la conception descendante consiste à affiner le concept abstrait de haut niveau en petites pièces concrètes et compréhensibles, jusqu'à ce que le plus petit bloc de construction soit défini. D'un autre côté, le bas vers le haut définit les parties de bas niveau, puis construit progressivement des blocs de niveau supérieur jusqu'à ce que tout le système soit formé.
Dans la pratique, il est préférable de combiner les deux méthodes: commence par une spécification de haut niveau pour spécifier pleinement la connaissance du domaine, sa relation et ses contraintes. Une fois le problème bien compris, les plus petits blocs de construction sont créés pour construire le système.
Le processus de:
- Création d'une spécification d'exigence
- Créer une spécification de conception (avec des diagrammes)
- Mettre en place
- Livrer
- Répéter (dans le développement itératif, plutôt que de faire un morceau entier dans chaque phase, nous en faisons un peu à plusieurs reprises et nous nous réunissons quotidiennement pour nous adapter aux exigences dynamiques du client)
me semble parfaitement normal (avec des spécifications comme des plans). Il a ses défauts, mais c'est pourquoi nous avons eu un développement itératif: au lieu de passer du temps sur une phase, dit l'analyse des besoins pour étudier tout ce qui est possible dans la connaissance du domaine qui est soumise à des changements (éventuellement quotidiennement), nous faisons un peu d'analyse, un peu de conception, puis l'implémenter.
Une autre façon est que chaque itération est une mode mini-cascade, où l'analyse se fait en quelques jours (ou une semaine). Il en va de même pour le design. Le reste du temps est consacré à la mise en œuvre. Y a-t-il quelque chose d'intrinsèquement mauvais dans l'approche descendante en combinaison avec le développement itératif?
Dans son essai Programming Bottom Up , Paul Graham semble encourager la construction de bas en haut complètement, ou la programmer de bas en haut, mais pas la phase d'analyse des exigences / conception:
Les programmeurs Lisp expérimentés répartissent leurs programmes différemment. En plus de la conception descendante, ils suivent un principe qui pourrait être appelé conception ascendante - changer la langue en fonction du problème.
Pour autant que je sache, ce qu'il voulait dire, c'est que Lisper exécute toujours une conception descendante, mais un programme ascendant, est-ce vrai? Un autre point qu'il a écrit:
Il convient de souligner que la conception ascendante ne signifie pas simplement écrire le même programme dans un ordre différent. Lorsque vous travaillez de bas en haut, vous vous retrouvez généralement avec un programme différent. Au lieu d'un seul programme monolithique, vous obtiendrez un langage plus grand avec des opérateurs plus abstraits et un programme plus petit écrit dedans. Au lieu d'un linteau, vous obtiendrez un arc.
Est-ce à dire que pendant la période d'écriture d'un programme en Lisp, vous vous retrouvez avec un outil générique?
Réponses:
De haut en bas est un excellent moyen de décrire des choses que vous savez ou de reconstruire des choses que vous avez déjà construites.
Le plus gros problème de haut en bas est que, bien souvent, il n'y a tout simplement pas de «sommet». Vous allez changer d'avis sur ce que le système doit faire lors de son développement et lors de son exploration du domaine. Comment peut être votre point de départ quelque chose que vous ne savez pas (c'est-à-dire ce que vous voulez que le système fasse)?
Un top down "local" est une bonne chose ... une certaine réflexion avant le codage est clairement bonne. Mais penser et planifier trop n'est pas, car ce que vous envisagez n'est pas le vrai scénario (à moins que vous n'ayez déjà été là-bas, c'est-à-dire si vous ne construisez pas, mais reconstruisez). Le top-down mondial lors de la construction de nouvelles choses est tout simplement absurde.
La méthode ascendante devrait être (globalement) l'approche, à moins que vous ne connaissiez 100% du problème, que vous ayez juste besoin de coder la solution connue et que vous ne vous souciez pas de rechercher des solutions alternatives possibles.
L'approche Lisp est la méthode ascendante distillée. Non seulement vous construisez de bas en haut, mais vous pouvez également façonner les briques comme vous en avez besoin. Rien n'est figé, la liberté est totale. Bien sûr, la liberté prend des responsabilités et vous pouvez faire des choses horribles en abusant de ce pouvoir.
Mais un code horrible peut être écrit dans n'importe quelle langue. Même dans des langues qui sont conçues comme des cages pour l'esprit, conçues avec l'espoir qu'avec ces langues, même les singes pourraient mettre en place de bons programmes (une idée si fausse à tellement de niveaux que ça fait mal même d'y penser).
Votre exemple concerne un serveur Web. Maintenant, en 2012, c'est un problème bien défini, vous avez des spécifications à suivre. Un serveur Web n'est qu'un problème d'implémentation. Surtout si vous visez à écrire un serveur Web substantiellement identique aux autres gajillions de serveurs Web qui existent, alors rien n'est vraiment flou, à l'exception de quelques détails. Même votre commentaire sur RSA parle toujours d'un problème clairement défini, avec des spécifications formelles.
Avec un problème bien défini, avec des spécifications formelles et des solutions déjà connues, le codage ne fait que se connecter entre les points. De haut en bas est ok pour ça. C'est le paradis du chef de projet.
Dans de nombreux cas, cependant, il n'y a pas d'approche bien connue et éprouvée à utiliser pour relier les points. En fait, il est très souvent difficile de dire même quels sont les points.
Supposons par exemple que l'on vous demande de charger une machine de découpe automatique d'aligner les pièces à découper sur un matériau imprimé qui n'est pas parfaitement conforme au logo répétitif théorique. On vous donne les pièces et les photos du matériel prises par la machine.
Qu'est-ce qu'une règle d'alignement? Tu décides. Qu'est-ce qu'un motif, comment le représenter? Tu décides. Comment aligner les pièces? Tu décides. Les pièces peuvent-elles être "pliées"? Cela dépend, certains non et certains oui, mais bien sûr pas trop. Que faire si le matériau est trop déformé pour qu'une pièce le coupe de manière acceptable? Tu décides. Tous les rouleaux de matériau sont-ils identiques? Bien sûr que non, mais vous ne pouvez pas inciter l'utilisateur à adapter les règles d'alignement pour chaque rouleau ... ce serait impossible. Quelles images voient les caméras? Le matériau, quoi que cela puisse signifier ... il peut être de couleur, il peut être noir sur noir où seul le réflexe de lumière rend le motif évident. Que signifie reconnaître un motif? Tu décides.
Essayez maintenant de concevoir la structure générale d'une solution à ce problème et donnez un devis, en argent et en temps. Je parie que même l'architecture de votre système ... (oui, l'architecture) sera fausse. L'estimation des coûts et du temps sera aléatoire.
Nous l'avons implémenté et maintenant c'est un système qui fonctionne, mais nous avons changé d'avis beaucoup de fois sur la forme même du système. Nous avons ajouté des sous-systèmes entiers qui ne sont même plus accessibles depuis les menus. Nous avons changé de rôle maître / esclave dans les protocoles plus d'une fois. Nous avons probablement maintenant suffisamment de connaissances pour tenter de le reconstruire mieux.
D'autres sociétés ont bien sûr résolu le même problème ... mais à moins que vous ne soyez dans l'une de ces sociétés, votre projet détaillé de haut en bas sera probablement une blague. Nous pouvons le concevoir de haut en bas. Vous ne pouvez pas parce que vous ne l'avez jamais fait auparavant.
Vous pouvez aussi probablement résoudre le même problème. Travailler de bas en haut cependant. Commencer par ce que vous savez, apprendre ce que vous ne savez pas et additionner.
De nouveaux systèmes logiciels complexes se développent, ne sont pas conçus. De temps en temps, quelqu'un commence à concevoir un gros nouveau système logiciel complexe mal spécifié à partir de zéro (notez qu'avec un grand projet de logiciel complexe, il n'y a que trois possibilités: a] la spécification est floue, b] la spécification est erronée et auto-contradictoire ou c] les deux ... et le plus souvent [c] est le cas).
Ce sont les projets typiques de grandes entreprises avec des milliers et des milliers d'heures jetées dans des diapositives PowerPoint et des diagrammes UML seuls. Ils échouent invariablement complètement après avoir brûlé des quantités de ressources embarrassantes ... ou dans certains cas très exceptionnels, ils fournissent finalement un logiciel trop cher qui ne met en œuvre qu'une infime partie des spécifications initiales. Et ce logiciel est toujours profondément détesté par les utilisateurs ... pas le type de logiciel que vous achèteriez, mais le type de logiciel que vous utilisez parce que vous y êtes forcé.
Est-ce à dire que je pense que vous ne devriez penser qu'à coder? Bien sûr que non. Mais à mon avis, la construction devrait commencer par le bas (briques, code concret) et devrait monter ... et votre concentration et votre attention aux détails devraient dans un sens "s'estomper" lorsque vous vous éloignez de ce que vous avez. De haut en bas est souvent présenté comme si vous deviez mettre le même niveau de détail à l'ensemble du système à la fois: il suffit de diviser chaque nœud jusqu'à ce que tout soit évident ... dans les modules de réalité, les sous-systèmes sont "développés" à partir de sous-programmes. Si vous n'avez pas d'expérience dans le problème spécifique, la conception descendante d'un sous-système, d'un module ou d'une bibliothèque sera horrible. Vous pouvez concevoir une bonne bibliothèque une fois que vous savez quelles fonctions mettre, et non l'inverse.
Beaucoup d'idées Lisp deviennent de plus en plus populaires (fonctions de première classe, fermetures, typage dynamique par défaut, ramasse-miettes, métaprogrammation, développement interactif) mais Lisp est toujours aujourd'hui (parmi les langues que je connais) assez unique dans la facilité avec laquelle il est possible de façonner du code pour ce dont vous avez besoin.
Par exemple, les paramètres des mots clés sont déjà présents, mais s'ils n'étaient pas présents, ils pourraient être ajoutés. Je l'ai fait (y compris la vérification des mots clés au moment de la compilation) pour un compilateur Lisp jouet que j'expérimente et il ne prend pas beaucoup de code.
Avec C ++, le plus que vous pouvez obtenir est un groupe d'experts C ++ vous disant que les paramètres des mots clés ne sont pas très utiles, ou une implémentation de modèle incroyablement complexe, cassée et à moitié sauvegardée qui n'est en effet pas si utile. Les classes C ++ sont-elles des objets de première classe? Non et vous ne pouvez rien y faire. Pouvez-vous avoir une introspection à l'exécution ou au moment de la compilation? Non et vous ne pouvez rien y faire.
Cette flexibilité linguistique de Lisp est ce qui le rend idéal pour la construction ascendante. Vous pouvez construire non seulement des sous-routines, mais aussi la syntaxe et la sémantique du langage. Et dans un sens, Lisp lui-même est ascendant.
la source
Je ne sais pas comment cette réponse s'appliquerait à Lisp, mais je viens de terminer la lecture des principes, modèles et pratiques agiles , et l'auteur, l' Oncle Bob , préconise fortement l'approche descendante pour C # (également applicable à C ++) avec laquelle j'ai pleinement se mettre d'accord.
Cependant, contrairement à certaines autres réponses qui ont permis de conclure que l'approche descendante signifie que dans la première interaction, vous ne fournissez que des documents et une conception globale, le livre pointe vers une autre méthode: TDD combiné avec une conception évolutive.
L'idée est que vous commencez par le haut et définissez vos niveaux d'abstraction les plus élevés (ou les plus élevés locaux) et dès qu'ils sont définis, vous leur faites un travail utile pour que la fonctionnalité # 1 fonctionne immédiatement. Ensuite, au fur et à mesure que vous ajoutez de plus en plus de fonctionnalités, vous refactorisez votre code et faites évoluer la conception selon vos besoins tout en restant toujours informé des principes SOLID. De cette façon, vous ne vous retrouverez pas avec trop de couches d'abstraction et vous ne vous retrouverez pas avec une conception de bas niveau qui ne correspond pas à l'architecture globale. Si vous n'êtes pas sûr de ce que cela signifie, le livre que j'ai mentionné ci-dessus contient un chapitre entier avec un exemple où les développeurs prennent des concepts et commencent avec des diagrammes UML et des classes de bas niveau, seulement pour réaliser que la moitié de ces classes ne sont pas nécessaires une fois que le codage commence réellement. Essentiellement avec cette approche, le code de bas niveau est naturellement poussé vers le bas à mesure que davantage de détails de haut niveau sont définis dans le projet.
Et enfin, si vous pratiquez SOLID, vous ne devriez pas vous retrouver dans une situation où vous avez défini des abstractions de haut niveau, puis entrer dans les détails et tout à coup trouver qu'il n'y a pas d'OOD ou d'abstractions. Ce n'est pas vraiment la faute de la conception descendante, mais de l'ingénierie paresseuse.
Si vous souhaitez en savoir plus sur XP et le design évolutif, voici une bonne lecture de Martin Fowler, un autre grand auteur: "Is Design Dead?"
la source
Pour moi, les remarques les plus importantes de Paul Graham dans son article sont les suivantes:
Ou, comme cela est connu dans les cercles C ++: la conception de bibliothèque est la conception de langage (Bjarne Stroustrup)
L'idée principale de la conception descendante est: d'abord vous planifiez, puis vous codez. Beanow a raison quand il écrit , qu'il y a des problèmes lorsque le code exécutable arrive tard dans le processus. Dans la conception ascendante, vous avez toujours du code et ce code qui peut être testé.
De plus, votre code n'est pas plat. J'entends par là, il a tendance à avoir plus de niveaux d'abstractions plus petites. Dans la conception descendante, les gens se retrouvent souvent avec de grandes abstrations jusqu'à un certain niveau arbitraire et en dessous, aucune abstraction du tout. Le code conçu de bas en haut OTOH contient souvent moins de contrôle de bas niveau et de structures de données car ils sont susceptibles d'être abstraits.
la source
Idéalement, l'écriture d'un programme dans n'importe quelle langue, pas seulement Lisp, vous permet d'écrire un ensemble complet d'outils génériques qui peuvent vous accélérer dans votre prochain programme ou améliorer la vitesse de votre programme actuel.
En pratique, le suivi de ces outils peut être difficile. La documentation est pauvre et désorganisée et les gens qui les connaissent partent. Dans la pratique, la réutilisation du code est souvent plus problématique qu'elle n'en vaut la peine. Mais si le code est documenté et organisé correctement et que les programmeurs restent (ou conservent leur propre source de code utile), une grande quantité de travail peut être économisée en récupérant le code de l'entrepôt plutôt qu'en le reconstruisant.
Tout le design doit être descendant, sinon vous ne sauriez pas ce que vous faisiez. Vous construisez un hangar ou une voiture? Vous ne pouvez pas comprendre celui-là avec un design ascendant. Mais si vous prévoyez de construire une roue avant gauche, vous pourriez penser que vous pourriez avoir besoin de plus de roues plus tard, à la fois pour ce projet et pour d'autres. Et si vous construisez une roue réutilisable, vous en aurez quatre pour le prix d'une. (Et 18 pour le semi-remorque que vous construisez ensuite, tout cela gratuitement.)
Notez que contrairement aux vraies voitures et aux vraies roues, si vous avez construit une "roue" logicielle, vous en avez construit un nombre infini.
Plus sur la conception descendante: bien que vous deviez commencer par cela, je me sens obligé de souligner que si vous ne pouvez pas construire une roue, vous devez le savoir avant de travailler beaucoup sur votre voiture. Vous devez donc travailler de bas en haut presque en parallèle avec le travail de haut en bas. De plus, savoir que vous pouvez construire une roue peut suggérer de nombreux projets auxquels vous n'auriez pas pensé auparavant, comme le semi-remorque. Je pense que l'approche descendante doit dominer, mais avec une touche très légère.
Donc, pour continuer à paraphraser Paul Graham, idéalement lorsque vous écrivez un programme, vous vous retrouvez avec beaucoup de pièces réutilisables qui peuvent gagner beaucoup de temps à la fois sur le programme d'origine et dans d'autres. Pour me distancier légèrement de Paul Graham, cela fonctionne dans n'importe quelle langue (bien que certains encouragent le processus plus que d'autres).
L'ennemi de ce processus presque magique sont les programmeurs avec des perspectives à très court terme. Cela peut résulter de défauts de personnalité, mais le plus souvent d'un changement de poste et de responsabilités trop rapide, à l'intérieur et entre les entreprises employeuses.
la source
L'utilisation d'une approche descendante avec le développement itératif ne fournit aucun code de travail dans les premières itérations. Il délivre des documents de conception et ceux sur lesquels le client aura du mal à donner son avis. Des réponses comme «ouais je suppose (c'est trop abstrait pour moi)» ne vous aideront pas à préciser davantage les spécificités du système souhaité par le client.
Au lieu de cela, vous créez uniquement un aperçu général de ce qui est demandé. (Exigences) à utiliser comme ligne directrice générale pour ce qui définit un produit fini et ce qui mérite d'être mis en œuvre en priorité. À partir de là, vous créez des produits fonctionnels pour chaque itération avec lesquels le client peut réellement jouer pour voir si c'était ce qu'il avait en tête. Sinon, ce ne sera pas un problème car il n'a pas été consacré des centaines d'heures à la conception d'un système qui fonctionne différemment de ce que le client demande maintenant.
Je ne parlerai pas pour une autre personne. Je n'ai pas non plus lu son essai. La réponse que je vous donne vient de mon éducation ainsi que de mes expériences.
L'utilisation de cette approche signifie que vous vous retrouverez avec des blocs de construction plus abstraits. Tout simplement parce que vous devrez créer des sous-systèmes autonomes qui peuvent être présentés immédiatement, vous ne pouvez pas créer une conception descendante qui est fortement entrelacée et doit être mise en œuvre en même temps. Il améliore la réutilisation, la maintenabilité mais surtout permet une réponse plus flexible au changement.
la source