IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Que pensez-vous de la loi de Demeter ?


Sujet :

Langage C++

  1. #101
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    octobre 2004
    Messages
    11 523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : octobre 2004
    Messages : 11 523
    Points : 29 967
    Points
    29 967
    Par défaut
    Non, ce n'est pas cela que je voulais dire :

    La voiture doit pouvoir répondre à deux (peut-être 3) questions existentielles concernant ses pneus :
    1. de combien de pneus as-tu besoin ? (j'en veux 7 : j'ai 3 essieux et la roue de secours )et
    2. quelle sont les dimensions de pneu que tu accepte (18"/180 ) ?
    3. (éventuellement, si cela a du sens : quels types de pneus acceptes-tu ?(de course) )

    A ces comportements, il suffit d'en rajouter un dernier qui permet de changer n'importe quel pneu:
    Remplace "tel " pneu par celui que je te donne ici.

    Si tu as ces trois (quatre ) comportements de base, tu as tout ce qu'il te faut pour sélectionner la liste des pneus potentiels et pour arriver à changer les pneus aussi bien par lot (on remplace "tel" par "tous") que séparément (on remplace "tel" par une information permettant de savoir qu'il s'agit du pneu avant droit).

    Maintenant, la manière dont la notion de pneu est, effectivement, représentée au sein du projet importe peu. Tu peux tout aussi bien lui transmettre un pointeur vers une classe de base "pneu" qu'un indice correspondant à la position du pneu en question dans une collection quelconque... Cela ne changera rien au problème (*)

    (*) A noter toutefois qu'il pourrait être intéressant que la voiture soit en mesure de vérifier la taille (et le type) de pneu qu'on veut lui faire monter, afin d'être en mesure de faire une assertion sur ces informations

    Et, bien sur, ce principe peut s'adapter aussi bien aux pneus, qu'aux sièges, aux portes, ou à tout ce que tu veux pouvoir "customiser" sur la voiture
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  2. #102
    Nouveau Candidat au Club
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    avril 2004
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : avril 2004
    Messages : 11
    Points : 0
    Points
    0
    Par défaut
    Bonjour,

    Je viens de publier un article sur le sujet : http://idd360.wordpress.com/2016/01/23/loi-de-demeter/

    Si ça peut alimenter le débat...

  3. #103
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    août 2003
    Messages
    5 273
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : août 2003
    Messages : 5 273
    Points : 10 827
    Points
    10 827
    Par défaut
    Déméter s'inscrit dans la continuité de l'abstraction -- c'est l'abstraction qui cache les détails et stabilise les interfaces, l'encapsulation assure les invariants.
    Quand ton agent expose 150 propriétés, et qu'il faut passer par ces dernières pour le manipuler, l'abstraction a fuit.

    Et quand tu parles du secrétaire, tu es encore en train de penser détail au lieux de t’adresser à une façade qui va redispatcher ton besoin à l'agent qui sait le résoudre. Pire, tu ne pourras pas t'adresser de manière identique à un cabinet (ce mot est important) qui a un(e) secrétaire, ou pas, ou carrément qui fait passer par une interface web pour prendre des RDV. Je n'ai que faire de mon interlocuteur pour prendre mon RDV tant qu'il est pris.

    Démeter est certes vite excessive, mais elle pousse dans une bonne direction quand on cherche à la suivre.

    PS: je ne vois pas le rapport avec la sémantique de déplacement, ni même avec a*X + Y, expressions templates ou pas. Je ne demande certainement pas à ma matrice une référence interne pour aller taper dedans.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  4. #104
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    octobre 2004
    Messages
    11 523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : octobre 2004
    Messages : 11 523
    Points : 29 967
    Points
    29 967
    Par défaut
    Ohhh, il y a tant à dire sur ton article, je vais encore une fois exploser la base de données

    Première ineptie :
    Disons-le d’emblée, Déméter est un leurre ! Une curiosité tout au plus.
    Non !!! Déméter nous permet de nous assurer que tous les services rendus par un type de donnée particulier (par une abstraction particulière) éviteront à l'utilisateur de celui-ci (de celle-ci) de faire des erreurs lorsqu'il manipulera une donnée de ce type.

    Ce n'est pas un leurre, c'est une réalité : les services fournis par une abstraction donnée doivent s'assurer que la donnée qui réagit à l'ordre qu'on lui donne sera dans un état cohérent après avoir réagi à cet ordre si elle était dans un état cohérent avant que l'ordre ne soit donnée.

    Bien sur, si la donnée n'était déjà pas dans un état cohérent avant que l'ordre ne soit donnée, on ne peut plus rien, mais c'est sans doute justement que Déméter n'a pas été respecté "ailleurs" et qu'on a laissé à l'utilisateur de notre type de donnée une "chance de faire une connerie"; chaque que l'utilisateur se sera empressé d'attraper en vertu de la loi de finagle

    deuxième ineptie :
    Avec Déméter, l’ensemble des services offerts par les composantes de la classe Agent doivent être répliqués au niveau de celle-ci. Cela peut faire un paquet de nouvelles méthodes !

    (emphasis is mine )
    Non!

    Ne devront être "répliqués" au niveau d'une classe utilisatrice que les services fournis par ses composants qui on du sens au niveau de la classe utilisatrice.

    Un exemple : une classe voiture qui utilise une classe réservoir. on se fout pas mal, lorsqu'on manipule une voiture de connaitre la capacité maximale du réservoir : les services que la classe voiture expose afin de pouvoir interagir avec le réservoir sont là pour s'assurer que, quoi qu'il arrive, nous ne pourrons pas dépasser la capacité maximale du réservoir.

    Le réservoir dispose d'une fonction maxCapacity() renvoyant la capacité maximale du réservoir C'est tout à fait normal : c'est l'un des services que l'on est en droit d'attendre de la part de ce type de donnée. Et c'est aussi un des services auquel les différentes fonctions membres de la classe voiture qui ont pour but de manipuler le réservoir risque de faire appel très régulièrement.

    Mais, au niveau de la voiture, l'idée est que nous n'avons absolument pas besoin que cette information soit "dévoilée" : les ordres que nous pourrons donner à notre voiture devront "simplement" veiller à renvoyer la quantité exacte de carburant qu'il nous a été possible de rajouter ou d'utiliser dans les limites admises par la capacité maximale

    troisième ineptie :
    Il faut bien comprendre que les composantes de la classe Agent peuvent avoir leurs propres composantes, et ainsi de suite. C’est un des avantages des types bien conçus, que de pouvoir être réutilisés facilement pour construire d’autres types. Agent::Savoir, par exemple, pourrait donner accès à une base de Faits, ainsi qu’à une liste d’Agents connus . Le principe de Déméter étant par essence récursif, ces sous-composantes vont à leur tour faire grossir le nombre de méthodes à ajouter dans Agent.
    Si l'on voulait répliquer tous les services au niveau de la classe utilisatrice, tu aurais sans doute raison.

    Or, comme il n'en est rien, tu as tout à fait tord sur ce point : si une classe A utilise une classe B et une classe C ; que la classe B utilise une classe D et une classe E et que la classe C utilise une classe F et une classe G sous une forme (chaque trait correspond à une relation "utilise" ou est utilisé par" ) proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
             A
           /   \
          B     C
         / \   / \
        D   E F   G
    tous les services proposés par D et par E ne sont pas forcément destinés à se retrouver dans B, et il en est de même pour les services proposés par F ou G au niveau de C. Si bien que l'aperçu que l'on peut avoir des services proposés par D, E , F ou G au travers de B ou de C est particulièrement restreint par rapport à la somme des services que les classe D, E, F et G peuvent fournir, et ce, malgré le fait que les services proposés par B puissent (ou non !!!) utiliser tous les services exposés par D ou par E et que ceux proposés par C puissent (ou non!!!) utiliser l'ensemble des services proposés par F ou par G.

    De même, les services proposés par A pourrons (ou non !!!) ** peut-être ** faire appel à tous les services proposés par B ou par C, mais il n'en restera pas moins que l'on ne retrouvera pas forcément l'ensemble des services de B (ou de C) dans l'interface de A

    Quatrième ineptie :
    Enfin, il faut comprendre que les types de ces composantes, conçus pour représenter des concepts importants du S.M.A., ont des chances d’être réutilisés en divers autres endroits. Par exemple, la classe Comportement pourrait également servir de composante à un type Machine. La loi de Déméter implique d’effectuer le même travail d’ajout de méthodes pour la classe Machine que celui réalisé pour Agent, occasionnant une certaine redondance quand bien même ces deux classes partagent des composantes de même type.
    De toutes évidences, tu n'arrive toujours pas à te défaire de l'habitude de penser en termes de données au profit d'une approche en termes de services.

    ON SE FOUT PAS MAL DES DONNEES QUI PERMETTENT A UNE CLASSE DE FOURNIR LES SERVICES QU'ELLE PROPOSE. Tout ce que l'on veut, c'est qu'elle propose un certain nombre de services, un certain nombre de comportements qui soient
    • cohérents par rapport au concept que l'on tente de modéliser
    • en mesure de garantir la cohérence du concept à l'exécution.



    Il se peut que deux concepts "complexes" utilisent en interne le même concept "plus simple", mais :
    1. il se peut que nos deux concepts "complexes" utilisent le concept plus simple d'une manière totalement différente et, si ce n'est pas le cas,
    2. la création d'une interface exposant les comportements communs à nos deux concepts "complexes" évitera tout risque de redondance

    Maintenant, si on s’intéresse au code client, il est vrai qu’avant Déméter, le code dépend de la classe Agent ainsi que de ses composantes. Après Déméter, le code client ne dépend plus que de la classe Agent seule, mais étant donné que celle-ci a vu son interface publique augmenter jusqu’à inclure l’ensemble des fonctionnalités disponibles auparavant via ses composantes, on a une autre forme de dépendance.
    En fait, avec une gestion dynamique des composantes de la classe Agent [penser unique_ptr], il est possible avant Déméter de ne faire voir au code client que les composantes effectivement manipulées, voire aucune le cas échéant. Avec Déméter, le code client voit systématiquement toutes les fonctionnalités possibles, même s’il n’en utilise aucune…
    Encore une fois, ton hypothèse de départ étant mauvaise, toute ta thèse s'écroule comme un château de cartes
    La création des nouvelles méthodes peut entraîner de sérieuses difficultés de nommage, particulièrement en cas de structure profonde.
    Pourquoi donc le nom d'une fonction doit exprimer clairement l'objectif poursuivi par la fonction.
    Et rien n'empêche d'avoir deux fonctions portant le même nom (car destinées à obtenir un résultat identique) mais utilisant des paramètres différents... Cela s'appelle : la surcharge de fonctions
    Si, du point de vue interne, la classe Agent conserve sa structure de composantes, du point de vue externe, le client ne voit que l’interface publique, et donc pour lui, Agent est devenue une classe monolithique, d’un abord difficile par conséquent.
    Si tu pars de l'hypothèse que tous les services exposés par les classes "composantes" sont forcément exposés par la classe "utilisatrice", alors, oui, en effet...

    Mais, encore une fois, ton hypothèse est fausse: tout nous incite à faire en sorte que la classe utilisatrice n'expose QUE L'ENSEMBLE MINIMAL INDISPENSABLE DES SERVICES QUE L'ON EST DECEMMENT EN DROIT D'ATTENDRE DE LA PART DU CONCEPT MODELISE. En deux mots : respecte également les principes SOLID (dont l'ISP en priorité), et tu te rendras compte que ton hypothèse de travail vole littéralement en éclats

    Finalement, et c’est le constat le plus sévère, Déméter tend à maximiser l’interface publique des classes, ce qui accroît directement la complexité globale du code…
    Si l'on s'en tient à la seule loi de Déméter, oui, peut être...

    Mais c'est faire preuve d'une bien mauvaise approche conceptuelle que de se limiter à l'usage de cette seule loi. Les principes SOLID sont d'égale importance par rapport à cette loi et vouloir appliquer l'un sans l'autre n'a absolument aucun sens!

    Revois ta conception, applique les principes SOLID en même temps que la loi de Déméter, et tu verras que toutes tes thèses ne tiennent absolument plus!

    Bon, je vais m'arrêter là, mais je ne dirais qu'une seule chose : ton approche est loin d'être assez complète et "conceptuellement cohérente" que pour que l'on puisse prendre tes conclusions au sérieux
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  5. #105
    Nouveau Candidat au Club
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    avril 2004
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : avril 2004
    Messages : 11
    Points : 0
    Points
    0
    Par défaut
    Citation Envoyé par Luc Hermitte Voir le message
    Déméter s'inscrit dans la continuité de l'abstraction -- c'est l'abstraction qui cache les détails et stabilise les interfaces, l'encapsulation assure les invariants.
    Quand ton agent expose 150 propriétés, et qu'il faut passer par ces dernières pour le manipuler, l'abstraction a fuit.
    Je me suis servi de l'exemple de l'Agent pour mettre en perspective ce qu'on obtient avec et sans Déméter.
    Dans un cas comme dans l'autre, le client à accès aux mêmes N services, en nombre identique. Seule la façon formelle d'y accéder change.
    - Avec Déméter, ces N services sont rendus disponibles de manière directe, via l'interface de la classe Agent. Celle-ci fonctionne bien comme une abstraction, en encapsulant sa structure interne, et permet donc au client de n'en rien connaître.
    - Sans Déméter (l'approche alternative que je propose), ces N services sont répartis entre plusieurs composantes de l'interface offerte par la classe Agent. Dans cette approche, on
    estime qu'il se dégage des groupes des fonctionnalités cohérents, relativement indépendants, et qui font sens dans le domaine métier. On estime donc qu'il est avantageux de fixer ces groupes de fonctionnalités par design, en en faisant des composantes de l'interface de Déméter. Il s'agit d'une simple structuration de l'interface. Dans cette approche, on estime que la structure figée de cette interface est plus un avantage qu'un inconvénient, du fait de la stabilité fonctionnelle de ces composantes. J'ai du reste montré que cette approche ne rendait pas le code client dépendant de la structure interne de la classe Agent, contrairement aux apparences. Le code client dépend seulement de la structure fonctionnelle (structure de l'interface).

    Quand tu dis "l'abstraction a fuit", on voit que tu prends comme hypothèse que la classe Agent doit forcément constituer une abstraction (de sa structure j'imagine).
    C'est peut-être simplement là où nous sommes en désaccord.

    Le point de mon article est de montrer que d'autres choix de conceptions peuvent être valides, et que le choix dépend de nombreux facteurs.
    Mais avec Déméter on ne choisit pas vraiment...

    Citation Envoyé par Luc Hermitte Voir le message
    Et quand tu parles du secrétaire, tu es encore en train de penser détail au lieux de t’adresser à une façade qui va redispatcher ton besoin à l'agent qui sait le résoudre. Pire, tu ne pourras pas t'adresser de manière identique à un cabinet (ce mot est important) qui a un(e) secrétaire, ou pas, ou carrément qui fait passer par une interface web pour prendre des RDV. Je n'ai que faire de mon interlocuteur pour prendre mon RDV tant qu'il est pris.
    Encore une fois, tu sembles penser a priori que, dans cet exemple très simple que je donne en début d'article, ce qu'on souhaite obtenir, c'est un design très souple qui puisse répondre à un maximum besoins potentiels, quelque chose d'adaptable facilement aux situations même inenvisagées.
    Le problème, c'est que ce n'est pas le cas. Je le sais car c'est moi qui ait choisi cet exemple, dans un but précis, en en fixant le périmètre.
    Le seul objectif de cet exemple est de prendre le contrepied de l'exemple du chien, simplement.
    L'exemple du chien veut montrer l'intérêt d'encapsuler le détail interne que constitue la jambe du chien. Il y a donc un parti-pris pour l'abstraction.
    Dans mon exemple du médecin et du secrétaire, je montre ce que cela donne sans parti-pris.
    Et dans le cas où le médecin ne prend jamais les RV lui-même, l'abstraction n'est effectivement pas intéressante, puisqu'il faut de toutes manières alors passer par le secrétaire.

    Abstraire à tout va peut paraitre séduisant, mais cela conduit à du sur-design.
    Mon expérience est que les types concrets, les relations fortes, ont autant d'importance que les abstractions.
    Refuser de fixer par design les relations les plus stables pour un problème donné, cela nous entraine où ?

    Citation Envoyé par Luc Hermitte Voir le message
    Démeter est certes vite excessive, mais elle pousse dans une bonne direction quand on cherche à la suivre.
    Je considère pour ma part quelle est inutile, car d'autres principes plus simples, plus fondamentaux, et surtout faisant consensus, suffisent à pousser dans la même direction, lorsqu'elle s'applique à juste titre.

    Citation Envoyé par Luc Hermitte Voir le message
    PS: je ne vois pas le rapport avec la sémantique de déplacement, ni même avec a*X + Y, expressions templates ou pas. Je ne demande certainement pas à ma matrice une référence interne pour aller taper dedans.
    Merci de me pointer le cas des expressions ! Sans doute pas de rapport.
    Je ne pensais pas à l'inspection d'une matrice, mais bien au chaînage d'opérations sur des objets temporaires hétérogènes. Après relecture de la définition formelle, ce type de chaînage semble ok ! Je vais donc retirer ce paragraphe... avec les rvalues references qui vont avec, bien sur.

  6. #106
    Nouveau Candidat au Club
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    avril 2004
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : avril 2004
    Messages : 11
    Points : 0
    Points
    0
    Par défaut
    Citation Envoyé par koala01 Voir le message
    ...
    La même en plus courtois, c'est possible ?
    Et en moins présomptueux par la même occasion ?

    Parce que là où tu parles d'"inepties" de ma part, j'y vois, moi, une lecture "rapide" de ta part...

    Quant à la crédibilité, ce sont les personnes qui s'expriment avec une agressivité déplacée qu'on ne prend pas au sérieux, en général.


    Je reste ouvert au débat cela dit et... merci malgré tout d'avoir passé tout ce temps.

  7. #107
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    octobre 2004
    Messages
    11 523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : octobre 2004
    Messages : 11 523
    Points : 29 967
    Points
    29 967
    Par défaut
    En quoi ai-je été discourtois dans mon message j'ai peut être été brutal dans le choix de mes termes, mais j'ai visiblement atteint mon objectif : te bousculer assez pour te forcer à réagir.

    En outre, je ne vois nullement où j'ai pu me montrer "présomptueux". je n'ai jamais essayé de me mettre en avant de manière inconsidérée, et je n'ai donné dans cet intervention aucun avis "d'autorité": j'ai à chaque fois essayé de justifier ma réaction .

    Et si tu trouves présomptueux de ma part de dire "je vais encore une fois exploser la base de données", saches qu'il 'est déjà arrivé plus souvent qu'à mon tour de vouloir poster une réponse et d'obtenir un message du genre "désolé, mais la taille des message est limitée à XXXXX (65 535, si mes souvenirs sont bons) caractères et votre message en fait (heu... entre "quelques uns" et "beaucoup" de plus). Cette phrase n'était donc rien de plus qu'un clin d'oeil au fait que je risquais -- encore une fois -- de vouloir écrire un roman que se serveur refuserait d'enregistrer pour cause de "nombre de caractères excessifs" .
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  8. #108
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    juin 2007
    Messages
    5 154
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : juin 2007
    Messages : 5 154
    Points : 16 968
    Points
    16 968
    Par défaut
    La loi de Demeter se résume par l'implémentation est un détail que l'utilisateur ne doit pas connaitre.

    Si la classe Voiture utilise une classe Réservoir ou gère tout elle-même, ca ne dois pas ce voir.
    C'est la raison pour laquelle il doit pas y avoir de fonction getRéservoir().

    Dans le même genre, avec une string message, tu écris message.substr(3,12), pas std::string(message.data()+3, message.data()+15), même si concrètement, c'est ce que fait (aux détails près) la fonction substr().
    Bien sur, la string contient forcément d'une manière ou d'une autre une C-string, mais l'utilisateur de std::string ne devrait pas avoir à y accéder lui-même.


    La loi de Demeter signifie précisément que les services rendus par une classe d'objets ne doivent pas être d'exposer un composant de ces objets.
    C'est une facette de l'encapsulation.

    De mon point de vue, cela rejoins immédiatement les sémantiques de valeur et d'entité.
    Une fonction membre ne devrait retourner que des valeurs (hormis les factory et autres patterns de créations).
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  9. #109
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    juin 2010
    Messages
    6 854
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : juin 2010
    Messages : 6 854
    Points : 31 459
    Points
    31 459
    Billets dans le blog
    4
    Par défaut
    Je trouve les exemples au mieux simplistes.
    On se moque pas mal de manipuler 1 chien, ou 1 agent. Si c'est pour faire quelque chose d'aussi simple, tu fais ce que tu veux. Même si déjà vouloir taper dans les composants pour le manipuler est franchement bancal amha.
    Maintenant si tu manipules une collection de chien, tu fais comment ? Du dynamic_cast partout pour récupérer le vrai chien, ou ses jambes, et le faire courir ? Ben non, tu appelles la méthode et tu le laisses faire sa tambouille interne (ou pas si tu as un chien sans jambe, trop gros pour courir, ...).
    Et face à ta collection de docteurs tu détermines s'il s'agit d'un cabinet, d'un type seul, s'il possède une ou plusieurs secrétaires ? Et s'il en a plusieurs, tu choisis comment la secrétaire par qui passer ? Tu dois ensuite consulter leurs agendas pour déterminer lesquelles sont disponibles ?
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  10. #110
    Membre averti

    Profil pro
    Inscrit en
    décembre 2013
    Messages
    323
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : décembre 2013
    Messages : 323
    Points : 429
    Points
    429
    Par défaut
    @igor_

    Pour commencer, tu fais un racourci (à mon avis) sur la loi de Déméter. Cette loi ne propose pas une solution ("Si A utilise B et que B propose le service X, alors A doit proposer le service X"), mais un "diagnostic" ("A ne doit pas exposer B"). La façon doit être réglé ce problème d'exposition des détails internes ne concerne par la loi de Déméter.

    En assimilant la loi de Déméter à cette solution (généralement bancale), il est effectivement facile de trouver des exemples qui sont bancales (avec des interfaces qui deviennent inutilement complexes). Mais cela ne rend pas caduque la loi de Déméter.

    Citation Envoyé par igor_ Voir le message
    Je me suis servi de l'exemple de l'Agent pour mettre en perspective ce qu'on obtient avec et sans Déméter.
    Dans un cas comme dans l'autre, le client à accès aux mêmes N services, en nombre identique. Seule la façon formelle d'y accéder change.
    C'est un autre point discutable de ton raisonnement. Ton exemple d'agent ne proposent pas d'autres rôles que "accéder à des composants internes". C'est une conception objet orientée "composition de données" et pas "rendre des services".

    Il est évident que des classes destinées à accéder à des composants internes (par exemple les pointeurs intelligents ou les conteneurs) ne peuvent pas respecter la loi de Déméter. Mais ce sont des exceptions connues à la loi de Déméter.

    (Je ne dis pas que tu as tort de concevoir ton agent comme cela. Si tu souhaites créer une lib de système multi-agent par exemple, tes agents seront peut être de simples conteneurs. Mais ce choix d'implémentation fait que la loi de Déméter ne s'applique pas dans ce cas).

    Citation Envoyé par igor_ Voir le message
    ce qu'on souhaite obtenir, c'est un design très souple qui puisse répondre à un maximum besoins potentiels, quelque chose d'adaptable facilement aux situations même inenvisagées.
    Le problème, c'est que ce n'est pas le cas.
    Ta remarque est vraiment étrange. En gros, tu dis que tu ne veux justement pas de design souple et qu'en faisant ce choix, la loi de Déméter est inutile... Ben oui, bien sûr !

    Les principes de conceptions visent à avoir du code respectant les critères de qualité logiciel (évolutivité, maintenabilité, etc). Si tu veux faire du code qui ne respecte pas ces objectifs, il est évident que les principes visant à obtenir ces résultats sont inutiles. Pour simplifier, ton message serait donc "si on veut faire du code de merde, les principes ne servent à rien". Ok, on est d'accord sur ce point.

    Maintenant, on pourrait discuter sur l'intérêt d'avoir du code de qualité ou non. Dans certains cas (prototypage ?), il est possible que la qualité du code n'est pas un point critique. Mais pour du code pro "standard", qui sera maintenu pendant des années, la qualité du code (et d'une bonne conception) est très critique. Et le respect de la loi de Déméter n'est pas sans intérêt dans ce cas.

  11. #111
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : juin 2012
    Messages : 1 711
    Points : 4 417
    Points
    4 417
    Par défaut
    Citation Envoyé par Luc Hermitte Voir le message
    Démeter est certes vite excessive, mais elle pousse dans une bonne direction quand on cherche à la suivre.
    Exactement, Démeter donne une bonne idée, mais il faut s'autoriser quelques exceptions.

    Citation Envoyé par mintho carmo Voir le message
    C'est un autre point discutable de ton raisonnement. Ton exemple d'agent ne proposent pas d'autres rôles que "accéder à des composants internes". C'est une conception objet orientée "composition de données" et pas "rendre des services".

    Il est évident que des classes destinées à accéder à des composants internes (par exemple les pointeurs intelligents ou les conteneurs) ne peuvent pas respecter la loi de Déméter. Mais ce sont des exceptions connues à la loi de Déméter.

    (Je ne dis pas que tu as tort de concevoir ton agent comme cela. Si tu souhaites créer une lib de système multi-agent par exemple, tes agents seront peut être de simples conteneurs. Mais le choix de l'implémentation fait que la loi de Déméter ne s'applique pas dans ce cas).
    +1.

    Un Agent expose des services tels que "onInit()"; "onTick()"; "onDie()"; "kill()";... Ces services sont bien dans la classe Agent, même si ils ne font que "rediriger" l'appel vers les composants.

    Pour aller récupérer les résultats sur les composants, un pattern visiteur marche bien (clairement ici, tout foutre dans la classe Agent n'est pas la solution).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    struct Component { virtual void onInit() = 0; };
    struct Component A: Component { /* ... */ };
    struct Component B: Component { /* ... */ };
     
    struct Agent {
       typedef std::unique_ptr<Component> component_ptr;
       std::vector<component_ptr> m_components;
     
       void onInit() { for(auto& c: m_components) { c->onInit(); } }
       // ...
    };
    A l'utilisation, on ne va pas fouiller à la main dans les composants pour appeler onInit() / onTick() / etc...

    La loi de Démeter me semble respectée ici.

  12. #112
    Nouveau Candidat au Club
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    avril 2004
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : avril 2004
    Messages : 11
    Points : 0
    Points
    0
    Par défaut
    @leternel


    Ce que tu dis est juste, mais tu décris essentiellement comment appliquer la loi de Déméter dans des cas où, à l'évidence, elle convient.

    Il est clair que string::data donne accès à un détail d'implémentation.


    Si, maintenant, on change de perspective, et on considère des types pour lesquels certains composants ne sont pas des détails d'implémentation, mais des composants avec lequels l'utilisateur préférerait interagir directement...

    Les containers font office d'exception à la règle de Déméter
    Tous les types n'ont pas la sémantique des containers, mais certains s'en rapprochent.
    C'est pour de tels types que l'application aveugle de la loi de Déméter peut être contre productive.

    Exemple typique : les widgets.
    Un widget n'est pas un container. Pourtant, en plus de ses fonctionnalités propres, il offre la possibilité à l'utilisateur d'accéder à ses sous-composants, d'une manière ou d'une autre,
    parfois par l'entremise d'un véritable container, parfois non.

    Appliquer Déméter ici est toujours possible : par exemple, on rassemble toutes les fonctionnalités possibles dans une interface unique - une façade - et l'utilisateur passe en paramètre un identifiant du sous-composant qu'il souhaite manipuler de manière transparente.
    Avec Déméter, la structure interne est abstraite, de même que les types mêmes des sous-composants.

    Tout ceci est très bien, mais est-ce ce que l'on veut vraiment ?

    Le fait est que, des frameworks de composants visuels que je connais, aucun n'a suivi la ligne Déméter, et il y a de bonnes raisons cela.

    Ton exemple :
    La modélisation d'une voiture et ses sous-composants peut très bien s'apparenter à celle d'une interface graphique... ou pas : tout dépend du but recherché, des besoins, de la façon dont on souhaite utiliser la voiture.
    Pour une simulation de conduite, proposer dans une interface unique les services rendus par la voiture est probablement une bonne idée.
    Pour une simulation industrielle, de nombreux composants doivent être modélisés et présentent leur propre interface, avec leurs propres fonctionnalités. Le code client va devoir manipuler ces composants, d'une manière ou d'une autre. Et les relations structurelles entre ces composants font aussi partie des choses que le client doit pouvoir inspecter.


    Plus généralement, Déméter fonctionne pour les systèmes à architecture fermée : on veut cacher la structure interne, considérée comme un détail d'implémentation.

    Mais d'autres systèmes présentent une architecture ouverte avec des composants stables qui représentent une abstraction du domaine métier.
    Déméter est contre-indiqué pour ces systèmes, et c'est ce je montre dans mon article.

  13. #113
    Nouveau Candidat au Club
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    avril 2004
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : avril 2004
    Messages : 11
    Points : 0
    Points
    0
    Par défaut
    Citation Envoyé par mintho carmo Voir le message
    @igor_

    Pour commencer, tu fais un racourci (à mon avis) sur la loi de Déméter. Cette loi ne propose pas une solution ("Si A utilise B et que B propose le service X, alors A doit proposer le service X"), mais un "diagnostic" ("A ne doit pas exposer B"). La façon doit être réglé ce problème d'exposition des détails internes ne concerne par la loi de Déméter.

    En assimilant la loi de Déméter à cette solution (généralement bancale), il est effectivement facile de trouver des exemples qui sont bancales (avec des interfaces qui deviennent inutilement complexes). Mais cela ne rend pas caduque la loi de Déméter.
    Soit, si je détecte un chaînage invalide selon la loi Déméter (A.getB().f(), Déméter me conduit à dignostiquer "A ne doit pas exposer B".

    C'est aussi ma compréhension de cette loi.
    Sauf que je suis en désaccord avec ce raisonnement, cette implication.

    Le propos de mon article est de montrer que de tels chaînages invalides peuvent être légitimes (en dehors des cas d'exceptions établis), en cas d'architecture ouverte (Cf mon post précédent).

    Et dans de tels cas, suivre Démeter va conduire à "ne pas exposer B". Quels que soient les moyens pour s'y prendre, cela va à l'encontre du principe de l'architecure ouverte choisi au départ pour A. C'est plus que contre-productif, c'est un non-sens, une contradiction. Et là je n'ai pas évoqué de "solution".



    Citation Envoyé par mintho carmo Voir le message
    C'est un autre point discutable de ton raisonnement. Ton exemple d'agent ne proposent pas d'autres rôles que "accéder à des composants internes". C'est une conception objet orientée "composition de données" et pas "rendre des services".

    Il est évident que des classes destinées à accéder à des composants internes (par exemple les pointeurs intelligents ou les conteneurs) ne peuvent pas respecter la loi de Déméter. Mais ce sont des exceptions connues à la loi de Déméter.

    (Je ne dis pas que tu as tort de concevoir ton agent comme cela. Si tu souhaites créer une lib de système multi-agent par exemple, tes agents seront peut être de simples conteneurs. Mais ce choix d'implémentation fait que la loi de Déméter ne s'applique pas dans ce cas).
    Je n'ai pas donné de détails sur les principaux types (Agent, Comportement...) de mon exemple, car cela aurait alourdi inutilement la présentation. J'ai juste dit qu'ils représentaient des concepts important du domaine métier. J'ai peu ou pas parlé des services propres à la classe Agent pour deux raisons :
    - ne pas perdre le lecteur entre les services directs et ceux indirects
    - les services directs ne sont en soi pas utiles à la démonstration.
    Pour autant, et de manière générale, quand on n'a qu'une vision restreinte d'un problème, il ne faut pas chercher à déduire ce qu'on ne peu pas déduire.
    Et naturellement, il n'y a pas de raison de penser que la classe Agent n'a pas vocation à proposer ses services propres.

    Et je réagis à l'aspect "orienté donné", que d'autres également semblent attribuer à mon exemple, à tort :
    Dans tout mon article, je parle essentiellement de services, non pas de données ou de propriétés. Cela veut dire ce que cela veut dire : les types de mon exemple du S.M.A. sont des orientés services avant tout.

    Je pense que certains font un amalgame entre architecture ouverte et structure de donnée. Vraiment, ce sont des choses différentre. Que les widjets d'un frameworks visuels soient tous des objets concrets, avec tout pleins de "propriétés", et copiables pour certains, ne les dispense pas pour autant de rendre de nombreux services, comme le fait de s'afficher. De même, ma classe Agent, pourrait offrir un service affiche().

    Citation Envoyé par mintho carmo Voir le message
    Ta remarque est vraiment étrange. En gros, tu dis que tu ne veux justement pas de design souple et qu'en faisant ce choix, la loi de Déméter est inutile... Ben oui, bien sûr !

    Les principes de conceptions visent à avoir du code respectant les critères de qualité logiciel (évolutivité, maintenabilité, etc). Si tu veux faire du code qui ne respecte pas ces objectifs, il est évident que les principes visant à obtenir ces résultats sont inutiles. Pour simplifier, ton message serait donc "si on veut faire du code de merde, les principes ne servent à rien". Ok, on est d'accord sur ce point.

    Maintenant, on pourrait discuter sur l'intérêt d'avoir du code de qualité ou non. Dans certains cas (prototypage ?), il est possible que la qualité du code n'est pas un point critique. Mais pour du code pro "standard", qui sera maintenu pendant des années, la qualité du code (et d'une bonne conception) est très critique. Et le respect de la loi de Déméter n'est pas sans intérêt dans ce cas.
    Je pense que tu n'as pas du tout compris le point, car mon article traite de la qualité logicielle avant tout.

    Comme je l'ai précisé dans ma réponse à Luc Hermitte, la conception n'est pas affaire d'abstractions uniquement, mais je pense qu'il le sait. Les types concrets, par exemples ont autant d'importance que les types abstraits. Celui qui en parle le mieux est probablement B. Stroustrup lui-même, donc je t'invite à lire ses livre. De la même manière, les relations fortes ont autant d'importance que les relations faibles (à faible couplage). Les unes s'articulent autour des autres. Il y a un équilibre à trouver. Les conceptions qui font la part belle aux abstractions en négligeant les relations fortes conduisent inévitablement à du sur-design, et c'est parfois pire que de l'optimisation prématurée, en terme de qualité logicielle.

  14. #114
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    octobre 2004
    Messages
    11 523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : octobre 2004
    Messages : 11 523
    Points : 29 967
    Points
    29 967
    Par défaut
    Il faut comprendre, igor_, que ce que l'on attend de la part de n'importe quelle classe est de fournir des services.

    On peut distinguer deux grandes catégories de services fournis par les classes :
    • la capacité de répondre à des questions particulières et
    • la capacité d'exécuter des ordres particuliers

    on peut envisager deux types de questions :
    Les "aides à la décision", comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    if(obj.isAlive())
       // je garde l'objet
    else
       // je jette l'objet
    qui, lorsque notre classe est en réalité un composant d'une classe plus complexe n'ont absolument aucune raison d'être exposées par la classe complexe : c'est un comportement (peut être interne à la classe complexe) qui décide de garder ou de jeter l'objet.

    D'un autre coté, tu as des questions qui permettent de transmettre différentes informations relatives à l'état (à un instant T) de l'objet en question. Encore une fois, la seule chose qui ait vraiment besoin de ce genre d'information est... l'objet qui manipule notre classe : si A manipule B, que B manipule C et que C dispose d'une comportement getColor(), B utilisera sans doute C::getColor() mais A n'aura normalement rien à foutre de la couleur de C dont il doit normalement ignorer l'existence.

    Il n'y aura que s'il est cohérent -- ce qui reste encore à démontrer -- propose lui-même un comportement getColor() qu'il y aura une raison valable pour créer ce service au niveau de B!

    Quelle que soit la situation, les seuls services que tu dois fournir au niveau d'une classe quelconque sont... Les services que tu es en droit d'attendre de la part de la classe que tu développe -- quel que soit le niveau d'accessibilité de ceux-ci -- et les services rendus par la classe utilisée n'ont -- a priori et à de rares exceptions près -- d'intérêt que pour la classe utilisatrice et non pour la classe qui utilise la classe utilisatrice.

    Alors, bien sur, il y a les classes ayant sémantique de valeur dont les seuls services que l'on est en droit d'attendre de leur part sont... de fournir les valeurs correspondant à leur détail d'implémentation. Mais ce type de donnée peut aisément être considéré comme un "type primitif au niveau de ton DSL" et tu n'enfreins pas forcément la loi de Déméter en décidant d'avoir une fonction renvoyant une position (au lieu d'une fonction renvoyant x, une deuxième renvoyant y et une troisième renvoyant z) ou une fonction renvoyant la couleur (au lieu d'avoir une fonction rouge, une fonction vert, une fonction bleu et une fonction aplha), à moins bien sur d'être dans une situation ou tu t'intéresse plus souvent à x ou à alpha qu'à n'importe quelle autre composant

    Mais, si tu y réfléchis bien : combien de classes ayant sémantique de valeur vas tu créer pour combien de classes ayant sémantique d'entité dans un projet Le rapport est généralement largement en faveur des classes ayant sémantique d'entité (ou, à défaut, devant être identifiables de manière strictement unique), non
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  15. #115
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    juin 2010
    Messages
    6 854
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : juin 2010
    Messages : 6 854
    Points : 31 459
    Points
    31 459
    Billets dans le blog
    4
    Par défaut
    Citation Envoyé par igor_ Voir le message
    Ton exemple :
    La modélisation d'une voiture et ses sous-composants peut très bien s'apparenter à celle d'une interface graphique... ou pas : tout dépend du but recherché, des besoins, de la façon dont on souhaite utiliser la voiture.
    Pour une simulation de conduite, proposer dans une interface unique les services rendus par la voiture est probablement une bonne idée.
    Pour une simulation industrielle, de nombreux composants doivent être modélisés et présentent leur propre interface, avec leurs propres fonctionnalités. Le code client va devoir manipuler ces composants, d'une manière ou d'une autre. Et les relations structurelles entre ces composants font aussi partie des choses que le client doit pouvoir inspecter.
    Je dirais surtout qu'un industriel, que ce soit une voiture ou un tracteur il s'en tamponne, il s'en sert surtout comme conteneur pour ses composants. Et qu'à ce jeu-là tu peux tout autant prendre std::vector pour exemple, mais comme dit plus haut ce serait juste faux.

    Citation Envoyé par igor_ Voir le message
    Et je réagis à l'aspect "orienté donné", que d'autres également semblent attribuer à mon exemple, à tort :
    Dans tout mon article, je parle essentiellement de services, non pas de données ou de propriétés. Cela veut dire ce que cela veut dire : les types de mon exemple du S.M.A. sont des orientés services avant tout.
    Tu y parles de service et les seuls exemples qu'on trouve sont Luc.getComportement().description(); "simplifié" en cout << Luc.descriptionDuComportement(); et Luc.getEtatInterne().actif() qui ne sont pas vraiment ce que j'appelle des services..
    Et quand bien même tu voudrais avoir une méthode "getBehaviorDescription" sur ton agent, ça n'en supprime pas pour autant l'intérêt de demeter. Ce serait tout au plus une curiosité, mais rien d'extraordinaire (parce que tu veux pouvoir lister les agents et avoir des infos à leur sujet, pour du suivi ou quoi que ce soit d'imaginable).

    Tu as beau introduire tout ça par
    Chaque Agent aura un Comportement, un Savoir, et un But,
    tout ce qu'on trouve derrière c'est des getter sur des propriétés. Aucune fonction Start, Update, qui sont typiquement ce qu'on attendrait d'un agent générique. Et chacun l'implémentant à sa sauce interne.
    Typiquement un isActif, chacun pourrait le définir comme il le souhaite, parce qu'aucun n'a besoin des mêmes états. Et vouloir figer les états possible c'est le meilleur moyen de devoir changer tout le code de tous les agents existants puis tout le code utilisant ces agents existants pour ajouter un misérable sous-état dans une nouvelle variation de comportement.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  16. #116
    Membre averti

    Profil pro
    Inscrit en
    décembre 2013
    Messages
    323
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : décembre 2013
    Messages : 323
    Points : 429
    Points
    429
    Par défaut
    Le problème est peut être que tu n'as pas lu ton propre article ?

    Citation Envoyé par igor_
    Le propos de mon article est de montrer que de tels chaînages invalides peuvent être légitimes (en dehors des cas d'exceptions établis), en cas d'architecture ouverte (Cf mon post précédent).
    Je te cite :

    Disons-le d’emblée, Déméter est un leurre ! Une curiosité tout au plus.
    Tu es passé d'une affirmation dans ton article à une possibilité dans ton dernier message. C'est pas exactement la même chose...

    Si tu avais étudié les cas limites de la loi de Déméter dans ton article, en montrant que cette loi admet des exceptions, cela ne poserait pas de problème. Et personne ne t'aurait contredit.

    Mais là, tu rejettes une loi complète, en avançant des exemples (bancals).

    (Et la loi de Déméter ne s’oppose pas au method chaining. Le meilleur exemple est probablement la majorité des opérateurs de la STL, qui retournent une référence sur le même objet et permettent d'appeler des méthodes en chaines).

    Citation Envoyé par igor_
    Dans tout mon article, je parle essentiellement de services, non pas de données ou de propriétés. Cela veut dire ce que cela veut dire : les types de mon exemple du S.M.A. sont des orientés services avant tout.
    Encore une fois, je te cite :

    Chaque Agent aura un Comportement, un Savoir, et un But, tous accessibles publiquement, au moins en lecture seule.
    Tu présentes bien ici ce que contient ta classe Agent, pas les services qu'elle fournit. Ou alors il faut que l'on se mette d'accord sur ce que veut dire "rendre des services". (De mémoire, je crois que koala01 en parle dans son livre, en disant qu'un service peut s'exprimer en utilisant un verbe. Il pourra confirmer si je me trompe pas).

    Je pense que certains font un amalgame entre architecture ouverte et structure de donnée. Vraiment, ce sont des choses différentre. Que les widjets d'un frameworks visuels soient tous des objets concrets, avec tout pleins de "propriétés", et copiables pour certains, ne les dispense pas pour autant de rendre de nombreux services, comme le fait de s'afficher. De même, ma classe Agent, pourrait offrir un service affiche().
    Puisque tu prends l'exemple des widgets, continuons dessus.

    Lorsque tu considères un widget, il sera effectivement (en général) dans une hiérarchie de widgets et pourra proposer l'accès à la liste de ses widgets enfants. (Par exemple, la méthode "children" de QObject).

    Mais cette hiérarchie de widgets n'est pas un "détail d'implémentation", mais une agrégation de données (voire une composition, par exemple dans Qt avec le système parent-enfants). (On pourrait discuter de l'intérêt de voir les widgets aussi comme une hiérarchie de widgets, mais c'est un détail par rapport à cette discussion sur Déméter).

    Dans les cas où les widgets proposent un service (QFileDialog propose le service "demander un nom de fichier", QComboBox propose le service "choisir dans un liste de choix", etc), alors les détails internes ne sont pas exposés. Tu ne sais pas si le drop-down button d'un QComboBox est implémenté avec un QPushButton et si le texte est implémenté avec un QLabel. Et tu n'as pas besoin de le savoir. Le loi de Déméter te dit que tu ne dois pas le savoir pour utiliser QComboBox. Proposer un accès au drop-down button pour pouvoir connecter le signal "clicked" (ce qui violerait Déméter) impliquerait qu'il ne serait pas possible de changer l'implémentation de QComboBox (par exemple, pour l'implémenter sans widgets enfant, juste avec paintEvent et mousePressEvent).

    Donc quand tu dis :

    Le fait est que, des frameworks de composants visuels que je connais, aucun n'a suivi la ligne Déméter, et il y a de bonnes raisons cela.
    je trouve au contraire que les frameworks graphiques respectent (en général) Déméter (modulo le fait que beaucoup de frameworks graphiques sont anciennes et se basent sur une conception parfois old-school - dont Qt).

    (Voir aussi les travaux en cours pour réécrire les QtQuick.Controls2, en changeant complètement l'implémentation interne, ce qui est possible justement parce que l'API ne change pas, grâce au respect de la loi de Déméter).

    Comme je l'ai précisé dans ma réponse à Luc Hermitte, la conception n'est pas affaire d'abstractions uniquement, mais je pense qu'il le sait. Les types concrets, par exemples ont autant d'importance que les types abstraits. Celui qui en parle le mieux est probablement B. Stroustrup lui-même, donc je t'invite à lire ses livre. De la même manière, les relations fortes ont autant d'importance que les relations faibles (à faible couplage). Les unes s'articulent autour des autres. Il y a un équilibre à trouver. Les conceptions qui font la part belle aux abstractions en négligeant les relations fortes conduisent inévitablement à du sur-design, et c'est parfois pire que de l'optimisation prématurée, en terme de qualité logicielle.
    <troll>C'est qui ce Stroustrup ? Je ne lis pas les livres du premier développeur C++ venu. Il s'y connait en C++ celui-là ?</troll>

    La loi de Déméter ne s'applique pas à un niveau d'abstraction particulier, mais aux implémentations. Déméter peut s'appliquer aussi bien aux types de base comme QWidget qu'aux types concrets comme QComboBox. Peu importe le type de relation (forte ou faible).

    Le sur-design est bien sûr un problème (bien que l'on rencontre plus souvent un mauvais design que le sur-design). Il faut savoir (et accepter) de ne pas aller trop loin dans la conception et (parfois) ne pas respecter un principe de conception. Mais il faut faire cela en connaissance de cause, en sachant que l'on introduit une faiblesse dans le design, qui ne sera pas (trop) problématique tant qu'elle reste localisée. Et qu'il faudra corriger en premier lieu cette faiblesse si l'application évolue.

    Pour autant, faire ce choix de ne pas respecter un principe n'invalide pas ce principe. Cela indique simplement que tant que les conséquences du non respect d'un principe reste minimes, on pourra les corriger plus tard.

    Note : bien sûr que la conception fait une part belle aux abstractions. Tout simplement parce une abstraction sera plus réutilisable qu'un code spécifique à un problème métier. Réussir à extraire d'un type concret les différents concepts qui le composent pour en faire des abstractions, cela permet de gagner en réutilisabilité (voir justement tous les travaux sur les concepts en C++).

  17. #117
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    octobre 2004
    Messages
    11 523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : octobre 2004
    Messages : 11 523
    Points : 29 967
    Points
    29 967
    Par défaut
    Citation Envoyé par mintho carmo Voir le message
    (De mémoire, je crois que koala01 en parle dans son livre, en disant qu'un service peut s'exprimer en utilisant un verbe. Il pourra confirmer si je me trompe pas).
    Oh, je dis énormément de choses concernant les services et les comportements, mais en gros :
    1. un verbe ou un (groupe verbal) apparaissant dans l'analyse fonctionnelle devra être traduit par une fonction dans le code
    2. une fonction correspond à un comportement observable, que l'on peut appeler "service rendu par la classe" si c'est une fonction membre et qu'elle est publique


    (ce ne sont que trois termes désignant en définitive la même chose à quelques "variation près" )
    Mais, comme je viens de le dire : il existe deux catégories de services / de comportements :
    • la capacité de répondre à une question
    • la capacité de réagir à un ordre qu'on lui donne.

    Pour ce qui est de la capacité à réagir à un ordre, il n'y a effectivement pas d'alternative : nous aurons recours à un verbe (ou à un groupe verbal) : cours!; déplace toi en; tires; attaques, ...

    Pour ce qui est des questions, bah... Les aides à la décisions dont je parlais plus haut renverront le plus souvent un booléen et utiliseront généralement des verbes comme "etre", "pouvoir", "devoir", "avoir" : isAlive(), canMove, needToBeUpdated(), hasChildren()

    Quant aux questions qui permettent simplement d'obtenir un état spécifique de la classe à un instant T (en gros, les accesseurs ), on a tendance à rajouter le verbe get (ce qui confirme l'impression qu'on utilise essentiellement des verbes), mais je préfères -- pour autant que possible -- tout simplement utiliser le terme équivalent à l'état recherché : maxQuantity au lieu de getMaxQuantity (mais on, cela revient finalement au même).

    Mais, ceci dit, étant donné qu'une comportement ou un service est une fonction à la base, et que l'on aura pris la décision de définir une fonction pour -- justement -- permettre au code d'avoir accès à ce que l'on a décrit au travers d'un verbe au niveau de l'analyse technique il est cohérent d'estimer que beaucoup de comportements seront identifiés par... le verbe correspondant à ce qu'il font
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  18. #118
    Nouveau Candidat au Club
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    avril 2004
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : avril 2004
    Messages : 11
    Points : 0
    Points
    0
    Par défaut
    Citation Envoyé par Bousk Voir le message
    Je dirais surtout qu'un industriel, que ce soit une voiture ou un tracteur il s'en tamponne, il s'en sert surtout comme conteneur pour ses composants. Et qu'à ce jeu-là tu peux tout autant prendre std::vector pour exemple, mais comme dit plus haut ce serait juste faux.
    L'industriel, utilisateur final, se "tamponne" de beaucoup de choses, en particulier des aspects techniques, et il a ses raisons.
    L'équipe de développement qui conçoit et maintient le système n'a pas les mêmes contraintes.
    Si l'architecture logicielle du coeur d'un simulateur pouvait se réduire à un simple vector listant des composants, le développement informatique serait une tâche bien plus simple qu'il ne l'ait.

    Il ne fait aucun doute qu'une conception aussi simple peut convenir parfaitement à de nombreux problèmes - je dirais des sous-problèmes, y compris dans le domaine de la modélisation automobile.
    Il est plus difficile de dire que cette simple liste puisse servir d'architecture à un simulateur complexe.

    Je ne vais pas essayer de te convaincre de l'existence de tels systèmes à architecture ouverte. Nous avons tous nos expériences. Mais les interfaces graphiques sont connues de tous, et presque tous les développeurs les ont pratiqué. Ce ne sont pas des structures de données, mais bien des constructions - des architectures - de composants imbriqués et organisés. Ces composants proposent des propriétés, mais également de services. Les frameworks proposent bien certains raccourcis pour accéder aux services d'un composants directement, par exemple en fournissant l'identifiant d'un Item de menu. Si ces raccourcis sont très pratiques et s'ils permettent effectivement de s'abstraire de la structure de la GUI (ce qui permet de respecter la loi de Déméter), ils restent l'exception : la plupart du code implémentant la GUI va en effet manipuler les composants en navigant dans la structure. Et il y a une bonne raison pour laquelle les frameworks visuels sont ainsi conçus : ils ne peuvent tout simplement pas prévoir tous les cas d'utilisation, et leurs combinaisons.


    Citation Envoyé par Bousk Voir le message
    Tu y parles de service et les seuls exemples qu'on trouve sont Luc.getComportement().description(); "simplifié" en cout << Luc.descriptionDuComportement(); et Luc.getEtatInterne().actif() qui ne sont pas vraiment ce que j'appelle des services..
    Comme je l'ai déjà dis:
    >Je n'ai pas donné de détails sur les principaux types (Agent, Comportement...) de mon exemple, car cela aurait alourdi inutilement la présentation. J'ai juste dit qu'ils représentaient des concepts important du domaine métier. J'ai peu ou pas parlé >des services propres à la classe Agent pour deux raisons :
    > - ne pas perdre le lecteur entre les services directs et ceux indirects
    > - les services directs ne sont en soi pas utiles à la démonstration.
    > Pour autant, et de manière générale, quand on n'a qu'une vision restreinte d'un problème, il ne faut pas chercher à déduire ce qu'on ne peu pas déduire.
    > Et naturellement, il n'y a pas de raison de penser que la classe Agent n'a pas vocation à proposer ses services propres.

    > Et je réagis à l'aspect "orienté donné", que d'autres également semblent attribuer à mon exemple, à tort :
    > Dans tout mon article, je parle essentiellement de services, non pas de données ou de propriétés. Cela veut dire ce que cela veut dire : les types de mon exemple du S.M.A. sont des orientés services avant tout.

    > Je pense que certains font un amalgame entre architecture ouverte et structure de donnée. Vraiment, ce sont des choses différentre. Que les widjets d'un frameworks visuels soient tous des objets concrets, avec tout pleins de "propriétés", et > copiables pour certains, ne les dispense pas pour autant de rendre de nombreux services, comme le fait de s'afficher. De même, ma classe Agent, pourrait offrir un service affiche().

    Citation Envoyé par Bousk Voir le message
    Et quand bien même tu voudrais avoir une méthode "getBehaviorDescription" sur ton agent, ça n'en supprime pas pour autant l'intérêt de demeter. Ce serait tout au plus une curiosité, mais rien d'extraordinaire (parce que tu veux pouvoir lister les agents et avoir des infos à leur sujet, pour du suivi ou quoi que ce soit d'imaginable).
    Une telle méthode, en soi, ne va pas supprimer l'intérêt de la loi Déméter, puisqu'elle permet justement de respecter ses contraintes.
    C'est ce qu'on obtient au final quand essaie d'appliquer Déméter sur une architecture ouverte telle que celle du S.M.A., que je dénonce.


    Citation Envoyé par Bousk Voir le message
    Tu as beau introduire tout ça par
    tout ce qu'on trouve derrière c'est des getter sur des propriétés. Aucune fonction Start, Update, qui sont typiquement ce qu'on attendrait d'un agent générique. Et chacun l'implémentant à sa sauce interne.
    Typiquement un isActif, chacun pourrait le définir comme il le souhaite, parce qu'aucun n'a besoin des mêmes états. Et vouloir figer les états possible c'est le meilleur moyen de devoir changer tout le code de tous les agents existants puis tout le code utilisant ces agents existants pour ajouter un misérable sous-état dans une nouvelle variation de comportement.
    Cf commentaire plus haut.
    Attention : je ne détaille pas les composantes dont je parle dans mon article, et c'est voulu. Il n'y a aucune raison de penser que, dans l'implémentation, ces types sont des types concrets. Cela sort du cadre du sujet de la loi de Déméter, mais il faut imaginer par exemple que Comportement peut être un type abstrait, géré par un unique_ptr. Cela permet à un Agent de ne pas avoir l'état de son Comportement "figé" (comme tu mentionnes). Les comportements spécifiques des Agents peuvent ainsi être implémentés de manière différente, en toute transparence pour le code de Agent ET client, qui ne manipulent qu'une composante abstraite.
    Ces considérations sont hors-sujet, mais je tenais à rectifier ce point.

  19. #119
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    octobre 2004
    Messages
    11 523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : octobre 2004
    Messages : 11 523
    Points : 29 967
    Points
    29 967
    Par défaut
    Citation Envoyé par igor_ Voir le message
    L'industriel, utilisateur final, se "tamponne" de beaucoup de choses, en particulier des aspects techniques, et il a ses raisons.
    L'équipe de développement qui conçoit et maintient le système n'a pas les mêmes contraintes.
    Si l'architecture logicielle du coeur d'un simulateur pouvait se réduire à un simple vector listant des composants, le développement informatique serait une tâche bien plus simple qu'il ne l'ait.

    Il ne fait aucun doute qu'une conception aussi simple peut convenir parfaitement à de nombreux problèmes - je dirais des sous-problèmes, y compris dans le domaine de la modélisation automobile.
    Il est plus difficile de dire que cette simple liste puisse servir d'architecture à un simulateur complexe.

    Je ne vais pas essayer de te convaincre de l'existence de tels systèmes à architecture ouverte. Nous avons tous nos expériences. Mais les interfaces graphiques sont connues de tous, et presque tous les développeurs les ont pratiqué. Ce ne sont pas des structures de données, mais bien des constructions - des architectures - de composants imbriqués et organisés. Ces composants proposent des propriétés, mais également de services. Les frameworks proposent bien certains raccourcis pour accéder aux services d'un composants directement, par exemple en fournissant l'identifiant d'un Item de menu. Si ces raccourcis sont très pratiques et s'ils permettent effectivement de s'abstraire de la structure de la GUI (ce qui permet de respecter la loi de Déméter), ils restent l'exception : la plupart du code implémentant la GUI va en effet manipuler les composants en navigant dans la structure. Et il y a une bonne raison pour laquelle les frameworks visuels sont ainsi conçus : ils ne peuvent tout simplement pas prévoir tous les cas d'utilisation, et leurs combinaisons.
    La plupart des bibliothèques (C++, s'entend) de GUI sont particulièrement mal foutues et témoignent, au mieux (mais c'est logique quand on voit leur histoire et l'époque à laquelle leur développement a commencé) d'une mauvaise compréhension du paradigme orienté objet, et, dans quasiment tous les cas, certains principes SOLID (dont le LSP en premier) sont purement et simplement jetés aux orties.

    Je ne remets ici absolument pas en question la qualité du travail effectué pour fournir ces bibliothèques, et j'apprécie énormément certaines d'entre-elles. Mais, de manière générale, la conception d'une bibliothèque d'IHM n'est vraiment pas le meilleur exemple à prendre
    Comme je l'ai déjà dis:
    >Je n'ai pas donné de détails sur les principaux types (Agent, Comportement...) de mon exemple, car cela aurait alourdi inutilement la présentation. J'ai juste dit qu'ils représentaient des concepts important du domaine métier. J'ai peu ou pas parlé >des services propres à la classe Agent pour deux raisons :
    > - ne pas perdre le lecteur entre les services directs et ceux indirects
    > - les services directs ne sont en soi pas utiles à la démonstration.
    > Pour autant, et de manière générale, quand on n'a qu'une vision restreinte d'un problème, il ne faut pas chercher à déduire ce qu'on ne peu pas déduire.
    > Et naturellement, il n'y a pas de raison de penser que la classe Agent n'a pas vocation à proposer ses services propres.
    Ben, dans ton article, la classe Agent a surtout vocation... d'exposer tous les services proposés par l'ensemble de ses composants.

    Or, ta classe Agent devrait avoir vocation à ... utiliser en interne les services proposés par les composants, de manière à ce qu'elle puisse se limiter à exposer ... que les seuls services que l'on est en droit d'attendre de sa part.
    > Et je réagis à l'aspect "orienté donné", que d'autres également semblent attribuer à mon exemple, à tort :
    > Dans tout mon article, je parle essentiellement de services, non pas de données ou de propriétés. Cela veut dire ce que cela veut dire : les types de mon exemple du S.M.A. sont des orientés services avant tout.
    Ben, oui et non!

    Tu pars (enfin, comme tu reste très vague sur le sujet, on va partir d'un a priori favorable )sur une approche orientée service pour les composants de ta classe Agent, mais, en voulant absolument faire en sorte que ta classe agent "réplique" tous les services de tous les composants qu'elle utilise, tu agis littéralement de la même manière que si tu suivais une logique orientée donnée. La seule différence est que tu pars du principe d'exposer les services fournis par les composant de ta classe Agent au lieu de donner accès à ces composants.

    Mais, au final, cela revient au même : tu penses à ta classe Agent en termes des données qu'elle manipule et non en termes des services qu'elle doit être en mesure de rendre à son utilisateur

    > Je pense que certains font un amalgame entre architecture ouverte et structure de donnée. Vraiment, ce sont des choses différentre. Que les widjets d'un frameworks visuels soient tous des objets concrets, avec tout pleins de "propriétés", et > copiables pour certains, ne les dispense pas pour autant de rendre de nombreux services, comme le fait de s'afficher. De même, ma classe Agent, pourrait offrir un service affiche().
    Au moins, un service affiche() ne nécessiterait pas que l'utilisateur sache exactement de quoi est composée ta classe Agent... Cette fonction utiliserait tous les composants de la classe Agent dont elle a besoin pour rendre le service attendu. Et respecterait parfaitement la loi de Déméter.

    Mais on est très loin de ce que tu présente dans ton article
    Une telle méthode, en soi, ne va pas supprimer l'intérêt de la loi Déméter, puisqu'elle permet justement de respecter ses contraintes.
    C'est ce qu'on obtient au final quand essaie d'appliquer Déméter sur une architecture ouverte telle que celle du S.M.A., que je dénonce.
    Au contraire : les SMA sont la cible parfaite pour mettre correctement Déméter en application!!!

    Car, tout ce que l'on attend de la part des agent est... de réagir (correctement) aux ordres que l'on est susceptibles de leur donner. Et donc, les seuls services "dignes" d'être exposés au niveau des agents sont... les ordres auxquels ils doivent pouvoir réagir et éventuellement quelques "aides à la décision" permettant à l'utilisateur de choisir de donner (ou non) un ordre particulier. Et l'utilisateur d'un agent particulier n'a ABSOLUMENT PAS A SAVOIR DE QUOI L'AGENT QU'IL UTILISE EST COMPOSE;
    Attention : je ne détaille pas les composantes dont je parle dans mon article, et c'est voulu. Il n'y a aucune raison de penser que, dans l'implémentation, ces types sont des types concrets. Cela sort du cadre du sujet de la loi de Déméter, mais il faut imaginer par exemple que Comportement peut être un type abstrait, géré par un unique_ptr. Cela permet à un Agent de ne pas avoir l'état de son Comportement "figé" (comme tu mentionnes). Les comportements spécifiques des Agents peuvent ainsi être implémentés de manière différente, en toute transparence pour le code de Agent ET client, qui ne manipulent qu'une composante abstraite.
    Ces considérations sont hors-sujet, mais je tenais à rectifier ce point.
    Cela n'a rien à voir !!! A partir du moment où tu considère que ton agent doit exposer la (quasi) totalité des services fournis par ses composants, tu fais purement et simplement fausse route : l'agent n'a à exposer que les services qu'on attend de sa part!

    Certains de ces services peuvent éventuellement correspondre à des services définis "tels quels" dans un composant donné de ton agent, mais cela reste l'exception! autrement, nous ne nous serions pas "cassé le cul" à regrouper différents composants au niveau de la classe Agent : nous aurions simplement utilisé ces composants séparément et "basta".

    Si on a décidé de regrouper plusieurs composants au niveau de la classe Agent, ce n'est que parce que l'on s'est bel et bien rendu compte que les différents composants devaient être utilisés conjointement à certaines occasions. Et les services exposés par ta classe Agent ne correspondent en réalité qu'à ... ces "occasions particulières" dans lesquelles les différents composants doivent être utilisés conjointement
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  20. #120
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    juin 2010
    Messages
    6 854
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : juin 2010
    Messages : 6 854
    Points : 31 459
    Points
    31 459
    Billets dans le blog
    4
    Par défaut
    Citation Envoyé par igor_ Voir le message
    L'industriel, utilisateur final, se "tamponne" de beaucoup de choses, en particulier des aspects techniques, et il a ses raisons.
    L'équipe de développement qui conçoit et maintient le système n'a pas les mêmes contraintes.
    On peut travailler dans la même équipe, sur des pans différents. Je n'en suis pas moins un utilisateur de ta classe, et son implémentation je m'en cogne tout autant que ce que tu appelles "utlisateur final".

    Citation Envoyé par igor_ Voir le message
    Comme je l'ai déjà dis:
    >Je n'ai pas donné de détails sur les principaux types (Agent, Comportement...) de mon exemple, car cela aurait alourdi inutilement la présentation. J'ai juste dit qu'ils représentaient des concepts important du domaine métier. J'ai peu ou pas parlé >des services propres à la classe Agent pour deux raisons :
    Mais on s'en moque que ton Agent possède un Comportement, But ou quoi que ce soit. Un Agent je lui demande doTask(); et qu'il utilise un Comportement interne ou autre n'est pas du tout mon souci.

    Citation Envoyé par igor_ Voir le message
    > - ne pas perdre le lecteur entre les services directs et ceux indirects
    > - les services directs ne sont en soi pas utiles à la démonstration.
    La grosse majorité des "services indirects" ne sont que des détails d'implémentation utilisés en interne pour réaliser le service demandé à l'Agent.
    Reprends ton exemple de secrétaire, en poussant plus loin l'absurdité
    - tu veux appeler ton médecin, qui a plusieurs secrétaires (on va partir de là pour simplifier, on sait qu'il a X secrétaires)
    - tu dois récupérer le planning de chaque secrétaire pour vérifier laquelle est présente
    - tu récupères son numéro et l'appelle enfin
    - tu récupères l'agenda du docteur pour vérifier quelle date est disponible
    - tu vérifies sur le bureau de la secrétaire si elle a un stylo
    - tu vérifies que le stylo n'est pas vide
    - tu mets le stylo dans la main de la secrétaire. attention, il te faut savoir si elle est droitière ou gauchère
    - ouf tu as ton rdv
    bien sur chacune de ses actions pourrait encore être découpée pour que l'exemple soit encore plus absurde

    Alors qu'un vrai service ressemblerait à
    - appeler le docteur
    -> dispatch de l'appel à la secrétaire disponible
    -> proposer dates disponibles
    - choisir une date de rendez-vous
    done

    Et si demain le docteur décide de changer quelque chose, ajouter/supprimer une secrétaire, que les agendas de rendez-vous sont maintenant électroniques, les bureaux, ... d'un côté tu dois reprendre ton code pour coller, de l'autre osef et seuls ceux qui implémentent ce flow auront à changer quelque chose
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

Discussions similaires

  1. Que pensez-vous des générateurs de doc PHP ?
    Par Nonothehobbit dans le forum EDI, CMS, Outils, Scripts et API
    Réponses: 64
    Dernier message: 10/07/2007, 11h17
  2. Que pensez vous de filemaker
    Par thpopeye dans le forum Autres SGBD
    Réponses: 4
    Dernier message: 14/06/2007, 16h20
  3. Que pensez vous du nouveau kernel 2.6 ?
    Par GLDavid dans le forum Administration système
    Réponses: 58
    Dernier message: 02/08/2004, 16h45
  4. [Débat] Que pensez-vous des langages à typage dynamique?
    Par Eusebius dans le forum Langages de programmation
    Réponses: 14
    Dernier message: 16/06/2004, 13h12
  5. Que pensez vous du mariage ASP Flash?
    Par tyma dans le forum Flash
    Réponses: 4
    Dernier message: 09/07/2003, 16h00

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo