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. #61
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par Jean-Marc.Bourguet Voir le message
    En passant, je vais redire ce que j'ai déjà écrit: les discussions sur une conception sans utilisation bien définie sont artificielles, on peut avoir des arguments pour tout et n'importe quoi et on n'a aucun moyen de juger des compromis. Hors la conception est un art du compromis.
    Salut,
    Je suis assez d'accord avec cette remarque. Sans quoi, tout n'est que rhétorique. Et c'est la différence entre apprendre des principes/méthodes à l'école et pouvoir en discuter après une pratique concrète.

    Ceci dit, pour Demeter, je tends à penser que, comme la plus part des principes, c'est au moment de la conception qu'il faut se poser la question de savoir ce qu'elle implique - ici, à mon sens, essentiellement le bon niveau d'abstraction dans les services proposés par une classe. Et si au codage on commence à la violer, il faut se demander pourquoi. On a peut être de bonnes raisons mais vaut mieux en être conscient.

    Je crois comprendre dans vos interventions, Loïc et Jean-Marc, que votre point de vue est que la classe n'est pas le bon niveau pour ce principe, mais que ce serait plutôt un 'groupe de classes' (une granularité entre la classe et la bibliothèque) car certaines classes sont trop dépendantes entre elles à l'intérieur de ce groupe. C'est ça ? Dans cette approche, la loi est violée à l'intérieur du groupe ou pour les utilisateurs du groupe ?
    Mettons qu'on a 3 classes A, B et C qui forment ce groupe. Il ne faut pas s'interdire dans A d'utiliser des fonctions d'un C obtenu via un B ? Ou si une classe D qui manipule un A conformément à LoD et qui obtient un B via un A peut utiliser les méthodes de B ? Ou les 2 ?

  2. #62
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    Je crois comprendre dans vos interventions, Loïc et Jean-Marc, que votre point de vue est que la classe n'est pas le bon niveau pour ce principe, mais que ce serait plutôt un 'groupe de classes' (une granularité entre la classe et la bibliothèque) car certaines classes sont trop dépendantes entre elles à l'intérieur de ce groupe. C'est ça ?
    Exact.

    Dans cette approche, la loi est violée à l'intérieur du groupe ou pour les utilisateurs du groupe ?
    Si la loi de Demeter c'est dans toute méthode M, on utilise uniquement le protocole des fournisseurs potentiel préférés de la méthode M, le fournisseur n'est pas la classe mais l'unite dont nous parlons et son protocole comprend des types, leurs membres publics et des fonctions libres (dont nous n'avons pas encore parle).
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

  3. #63
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par Jean-Marc.Bourguet Voir le message
    Si la loi de Demeter c'est dans toute méthode M, on utilise uniquement le protocole des fournisseurs potentiel préférés de la méthode M, le fournisseur n'est pas la classe mais l'unite dont nous parlons et son protocole comprend des types, leurs membres publics et des fonctions libres
    Donc, c'est bien les 2. Le groupe est vu comme le fournisseur.
    Citation Envoyé par Jean-Marc.Bourguet Voir le message
    (dont nous n'avons pas encore parle).
    Pourquoi cette remarque ? Il y a une particularité sur les fonctions libres ?

  4. #64
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    Pourquoi cette remarque ? Il y a une particularité sur les fonctions libres ?
    En ne parlant que de classes et de methodes, la loi de Demeter les ignore totalement. Hors elles font partie du protocole propose.
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

  5. #65
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    C'est que définie au niveau de la classe, la loi interdit la transitivité en quelque sorte : on ne peut utiliser son protocole, d'un de ses membres, d'un objet obtenu en paramètre, d'un objet créé ou d'un objet globale. Ce qui n'est pas explicitement autorisé étant implicitement interdit, l'idée est qu'on ne peut invoquer le protocole d'un objet obtenu via un autre objet.
    Je ne vois pas quel sens cela peut prendre avec une fonction libre ?

  6. #66
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 081
    Points
    16 081
    Par défaut
    Je prends le train en marche

    Que pensez-vous de la loi de Demeter ?
    Je pense que c'est une loi qui date un peu (1987). Elle a pour but de découpler au maximum les objets. Mais elle découple un peu trop a mon gout, car elle est très stricte et donc très contraignantes (Transmogrifier/delegation partout).

    Depuis 1987, les "bonnes pratiques" en matière de découplage ont un peu évoluées : On recherche plus le compromis low coupling/high cohesion.

    Je préfère des approches plus souples comme le Dependency inversion principle.
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  7. #67
    Membre confirmé Avatar de Lavock
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    560
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 560
    Points : 633
    Points
    633
    Par défaut
    Hum, je la savais pas aussi vieille Oo...

    Citation Envoyé par Jean-Marc.Bourguet Voir le message
    En passant, je vais redire ce que j'ai deja ecrit: les discussions sur une conception sans utilisation bien definie sont artificielles, on peut avoir des arguments pour tout et n'importe quoi et on n'a aucun moyen de juger des compromis. Hors la conception est un art du compromis.

    Entièrement d'accord avec toi, j'aurais juste aimais montrer que même sans Demeter, on peut faire quelque chose de "propre", universellement parlant... rien d'autre.

    D'accord avec le fait que la loi devient largement applicable au niveau du "package". Je dirai même que ça me paraît plus logique( et c'est vers ça que mes programmes convergent habituellement d'ailleurs).
    Au passage, ça ne serait pas plus proche du découpage High level/Low level de se qu'on trouve dans les patterns plus "neufs" ?

    Et au fait,

    Citation Envoyé par 3DArchi Voir le message
    ...Et c'est la différence entre apprendre des principes/méthodes à l'école et pouvoir en discuter après une pratique concrète.
    Si c'est une critique, je la trouve très déplacé... en plus, c'est même pas vraiment adapté à mon cas... Mais je t'aime bien quand même :p !
    The mark of the immature man is that he wants to die nobly for a cause, while the mark of the mature man is that he wants to live humbly for one.
    --Wilhelm Stekel

  8. #68
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    T'étais pas visé amha. Sinon y'en a pas mal qui peuvent se sentir offensé =).
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  9. #69
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par pseudocode Voir le message
    Je pense que c'est une loi qui date un peu (1987). <snip>
    Citation Envoyé par Lavock Voir le message
    Hum, je la savais pas aussi vieille Oo...
    En fait, elle est même encore bien plus vieille que cela...

    Ce n'est peut être qu'en 1987 qu'elle a refait surface dans le domaine de la programmation, mais elle avait été appliquée bien avant cela (d'une certaine manière, la bombe atomique lancée sur hiroshima a été construire en respectant cette loi, par exemple, et on peut trouver quantité d'exemples dans lesquels elle ait été appliquée dans des domaines très variés )
    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

  10. #70
    Membre expert
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 717
    Points : 3 344
    Points
    3 344
    Par défaut
    A propos du groupe de classe, en fait personellement j'aurais plutot dit que la loi de Demeter s'applique bien au niveau de "systèmes". (je ne sais pas si vous utilisez le même terme)
    Par là j'entends soit une classe, soit une fonction, soit un groupe de classe/fonction qui sont fait pour marcher ensemble et qui ensemble exposent une interface publique au reste du monde qui elle doit obeir a la loi de Demeter pour être utilisable simplement avec le reste du monde puisque cette interface ce sont les services que propose ce système.

    Concernant le fait de briser la loi, je suis tombé récemment sur ce même dilemme ou j'aurais du l'appliquer mais je ne l'ai pas fait finalement. Je pense que ça doit être un exemple assez courant vu que ce n'est pas la première fois que je tombe dessus: j'ai une classe qui représente l'état d'un jeu, appelont la World pour l'exemple. Elle contient des objets membres "managers" qui manipulent les différents types d'elements du jeu et ont donc chacun une part de l'état du jeu -- en fait chacun fourni les manipulations de ces éléments et effectuent les vérifications et notifications nécessaires. Je me suis concentré sur l'écriture de chacun des manager/sous-système séparément pour bien séparer les concepts. J'en suis arrivé au point ou toutes les parties étaient completes séparément mais ou il fallait les rassembler dans l'objet World pour que tout soit "ensemble".
    Je me suis donc retrouvé avec quelque chose comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    class World
    {
    public:
        World( const std::string& name );
     
     
         const std::string& name() const { return m_name;}
     
    private:
     
        const std::string m_name;
     
        TrucManager m_truc_manager;
        MachinManager m_machin_manager;
        BiduleManager m_bidule_manager;
        WorldMap m_map;
     
     
    };

    Quand j'écrivais le code les classes WorldMap, BiduleManager, MachinManager, TrucManager, j'étais dans l'optique de suivre la loi de Demeter qui habituellement me viens naturellement (j'imagine que c'est a force d'explorer les alternatives).
    Or, arrivé au point au dessus, après avoir écrit environ une vingtaine de fonctions membre par classe membre de World, qui doivent toutes être accessible a partir d'un World (non-const)... je me suis dit que ça serait certainement un effort inutile et redondant d'écrire en membre de World chacune des fonctions membres publiques des autres classes.

    Le simple fait d'avoir a maintenir 2 fonctions au lieu d'une lorsque j'ai besoin de changer l'interface d'une de ces classes me rebutte naturellement.

    Une alternative simple aurait été de faire hériter World des autres classes. J'écarte cette solution simplement parceque l'héritage ici ne me semble pas très logique, d'un point de vue des concepts mis en place et parcequ'en réalité World est bien juste un conteneur permettant de situer le contexte commun aux données qu'elle contiens. World n'est pas lui même une WorldMap par exemple. Par contre elle contiens une WorldMap. Donc pas d'héritage.

    Aussi pour l'instant, dans le doute (j'hésite encore a tester d'autres solutions) j'ai juste fais :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    class World
    {
    public:
        World( const std::string& name );
     
     
         const std::string& name() const { return m_name;}
     
        TrucManager& truc_manager() { return m_truc_manager; }
        MachinManager& machin_manager() { return m_machin_manager; }
        BiduleManager& bidule_manager() { return m_bidule_manager; }
        WorldMap& map() { return m_map; }
     
        const TrucManager& truc_manager() const { return m_truc_manager; }
        const MachinManager& machin_manager() const { return m_machin_manager; }
        const BiduleManager& bidule_manager() const { return m_bidule_manager; }
        const WorldMap& map()  const { return m_map; }
     
    private:
     
        const std::string m_name;
     
        TrucManager m_truc_manager;
        MachinManager m_machin_manager;
        BiduleManager m_bidule_manager;
        WorldMap m_map;
     
     
    };
    Si je considère ce que je disais au début, a propos du fait que ça s'applique aux "systèmes", alors cette solution suit bien la logique de la loi de Demeter : les classes TrucManager, MachinManager, BiduleManager et WorldMap sont ici déjà des systèmes complets et doivent donc suivre chacune la loi en exposant les "verbes" que sont leur services/fonctions respectifs. World n'étant qu'un conteneur, il n'expose pas de "verbe" et ne fait que donner un contexte commun.

    Suivant cette logique, je me refuse a ajouter des fonctions membres dans World qui seraient par exemple des fonctions helper manipulant les différents membres (sauf un eventuel clear() histoire de tout remettre a plat au besoin).
    En fait, il serait faux de dire que World a une sémantique de valeur, mais a part l'absence d'opérateur et autres spécificitées de ce type de sémantique, ça y ressemble fortement (après tout, c'est juste un "état", une "valeur" qui peut être clonée).

    Cela me permet de facilement construire autour de World d'autres systèmes qui manipulent cet état (selon les limites imposées par les membres de World) sans complication puisqu'il est assez logique d'avoir par exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    World world; 
    //...
    world.map().is_wall( x, y );
    //...
     
    Truc& truc = world.truc_manager().create( TrucType::SUPER_TRUC );
    (En fait ce qui peut gêner ici c'est peut être le fait qu'une fonction ne soit qu'un accesseur alors qu'on voudrait dans l'idéal que toute fonction soit un verbe (les parenthèèses font tache). Mais en C++ on a pas --encore-- d'accesseurs implicite donc bon...)

    J'ai donc par exemple une classe WorldVM qui manipule un world en le mettant régulièrement à jour et en appliquant des règle de déplacement dans le temps par exemple.
    Je peux aussi avoir un WorldEdit qui manipule un World uniquement pour "l'éditer", faisant office d'interface avec un système d'interface graphique.

    Au fond, ce sont chacun de ces systèmes qui exposent leurs services. Ils sont souvent représentés par une classe, mais plus souvent encore par un ensemble de classes/fonctions. Une classe à un rôle unique, un système expose des services liés à un domaine qui lui aussi peut être un rôle. Pour moi cette notion de système est flexible et rends la loi de Demeter plus claire si on l'applique à son niveau (qui varie donc selon ce dont est composé le système).

    Si dans le cas de mon exemple vous avez une meilleure solution, j'envisage toujours la possibilité que je n'ai pas vu une meilleure façon de faire, donc n'hésitez pas a l'exposer.

  11. #71
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    Moi, ça me fait voir World comme un conteneur et comme tous les conteneurs, la loi de Demeter ne s'applique pas vraiment. En fait, probablement que :
    1/ Il n'y a qu'une instance de World dans ton appli.
    2/ World n'est pas copiable.
    3/ Que World n'a pas de protocole particulier autre que manipuler ses éléments.
    Et certainement que tu gagnerais à passer directement ses membres en public plutôt que de n'avoir que des accesseurs vers eux. Ce serait plus explicite.

  12. #72
    Membre expert
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 717
    Points : 3 344
    Points
    3 344
    Par défaut
    Exact, il revient effectivement a une sorte de conteneur. Toutefois:

    1) Il peut y en avoir plusieurs.
    2) Il est "clonable" au sens ou toutes les instances du jeu ont des identifiant et des paramettres sérializables, donc on peut le copier (même si je n'ai pas fait de fonction explicite de copie, ce qui serait de toutes façons très simple a faire actuellement, mais je n'en ait pas encore eu besoin donc YAGNI).
    3) Exact.

    J'ai effectivement pensé a mettre le tout en publique. J'hésite a le faire pour l'instant mais j'y viendrait a force je pense.

  13. #73
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Personnellement, je vois plus cette classe comme une God Class que comme une classe conteneur. Cette classe est tellement chargée qu'elle est utilisée partout dans le code, je suppose - avec la conséquence que si le code a besoin d'avoir accès à un manager, il va devoir se résoudre à dépendre des autres quand même.

    Le problème de ce design n'est pas encore lié à l'utilisation de la loi de Demeter, mais bien à l'architecture elle-même. Le premier problème, c'est la notion de manager. Si on essaye de répondre à la question "que fait un manager de X", la réponse est invariablement "il gère des X". Gérer ne voulant rien dire (pas de définition précise ; ou plus exactement, tellement de définition qu'on se retrouve avec un petit problème de sémantique...). A la base, un manager est nécessairement une god class, du fait même de son nom. On s'en sert donc partout. Puisqu'on s'en sert partout, la tentation est grande de grouper les managers dans une classe, ce qui transforme celle-ci en hyper-god class, qui est utilisée encore plus partout. A terme : le code devient impossible à maintenir car le moindre changement dans un manager risque d'avoir un impact sur l'ensemble du code.

    Si tu veux voir à quel point World est une god class, commence par encapsuler tous les appels à chacune des fonctions du manager par des méthodes dans World (c'est la base de la loi de Demeter, après tout). TU va rapidement t'apercevoir que le nombre de méthodes explose littéralement.

    Je n'aime pas les manager. Ils sont trop étendus en termes de responsabilités (on en revient toujours à ça dans cette discussion ), sans pour autant définir quel est leur rôle exact. On finit par en avoir tellement marre de leur omniprésence qu'on les transforme en singletons, parce que bon, hein, il ne peut en rester qu'un... Ce n'est pas toujours le cas (parce que des fois, on peut trouver sympathique d'avoir plusieurs managers du même type) mais dans la pratique, ça se termine souvent comme ça quand même.

    Dès lors qu'on commence à se dire "et là, je vais mettre un manager de X", il faut se poser la question de pourquoi. A quoi va servir ce manager ? Quelles seront ses fonctions ? N'y a-t-il pas un découpage plus sensé, qui permettrait très certainement un plus grand découplage ?

    Si on reprends un exemple classique - une "manager de ressources" dans un programme basé sur DirectX, ce manager est une combinaison d'au moins trois fonctions différentes : une fonction de factory, une fonction de collection, et une fonction de contrôle de la durée de vie. Bon nombre d'algorithmes n'ont pas besoin de connaitre la factory et la fonction de contrôle de la durée de vie (par exemple, la récupération sur une erreur device lost). S'il est évident (et encore) que la factory et le contrôle de la durée de vie peuvent s'appuyer sur la collection (l'un pour ajouter des éléments, l'autre pour les supprimer), il n'en reste pas moins que les fonctions ne sont pas liées entre-elles. La fonction de contrôle de la durée de vie est totalement indépendante de la fonction factory, et inversement.

    Bon nombre de managers sont en réalité mieux représentés par plusieurs classes. En ajustant au mieux le découpage d'un manager, on affine aussi son utilisation, qui est nécessairement moins globale que l'utilisation du manager complet. Ce qui permet de réduire le couplage entre les classes. Ce qui permet de ne pas avoir à créer de god class. Ce qui permet de rendre la loi de Demeter plus efficace.
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  14. #74
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 081
    Points
    16 081
    Par défaut
    Le problème avec les conteneurs c'est qu'il y a inversion de controle (ioc). Et donc inversion de la chaine de dépendance.

    Sans l'utilisation d'un conteneur, on aurait sans doutes des instances de Manager qui utiliseraient une instance de World (par ex. passé en injection au constructeur).
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  15. #75
    Membre confirmé
    Inscrit en
    Août 2004
    Messages
    556
    Détails du profil
    Informations forums :
    Inscription : Août 2004
    Messages : 556
    Points : 588
    Points
    588
    Par défaut
    Tiens en parlant de Manager, pour un projet sur lequel je suis sur l'analyse, j'ai finit par décider que le Manager est la meilleure solution (un manager par type d'Entité).
    Citation Envoyé par Emmanuel Deloget Voir le message
    Je n'aime pas les manager. Ils sont trop étendus en termes de responsabilités (on en revient toujours à ça dans cette discussion ), sans pour autant définir quel est leur rôle exact. On finit par en avoir tellement marre de leur omniprésence qu'on les transforme en singletons, parce que bon, hein, il ne peut en rester qu'un... Ce n'est pas toujours le cas (parce que des fois, on peut trouver sympathique d'avoir plusieurs managers du même type) mais dans la pratique, ça se termine souvent comme ça quand même.

    Dès lors qu'on commence à se dire "et là, je vais mettre un manager de X", il faut se poser la question de pourquoi. A quoi va servir ce manager ? Quelles seront ses fonctions ? N'y a-t-il pas un découpage plus sensé, qui permettrait très certainement un plus grand découplage ?

    Si on reprends un exemple classique - une "manager de ressources" dans un programme basé sur DirectX, ce manager est une combinaison d'au moins trois fonctions différentes : une fonction de factory, une fonction de collection, et une fonction de contrôle de la durée de vie. Bon nombre d'algorithmes n'ont pas besoin de connaitre la factory et la fonction de contrôle de la durée de vie (par exemple, la récupération sur une erreur device lost). S'il est évident (et encore) que la factory et le contrôle de la durée de vie peuvent s'appuyer sur la collection (l'un pour ajouter des éléments, l'autre pour les supprimer), il n'en reste pas moins que les fonctions ne sont pas liées entre-elles. La fonction de contrôle de la durée de vie est totalement indépendante de la fonction factory, et inversement.

    Bon nombre de managers sont en réalité mieux représentés par plusieurs classes. En ajustant au mieux le découpage d'un manager, on affine aussi son utilisation, qui est nécessairement moins globale que l'utilisation du manager complet. Ce qui permet de réduire le couplage entre les classes. Ce qui permet de ne pas avoir à créer de god class. Ce qui permet de rendre la loi de Demeter plus efficace.
    Et en découplant la gestion de durée de vie de la gestion de l'entité, tu changes quoi, concrètement ?

    Mon design se rapproche de celui de Klaim sauf qu'il diffère dans le sens où je n'ai pas de classe "World". J'ai des composants Positionable, Drawable, etc... Et quand une entité est crée, chacune de ses composants va notifier son manager respectif qu'il est crée. Lorsqu'il meurt, il va notifier son manager de ça aussi. Et c'est à peu près tout au niveau de l'entité. Au niveau du manager, il va juste "stoker" un pointeur vers ces entités dans sa liste, et appeller son protocole de routine quand il veut demander à l'entité de se mettre à jour. Parallèlement, un gestionnaire de messages inter-entités est mis en place afin que les entités puissent communiquer.
    Ce qui veut dire que l'entité va effectivement dépendre de 2 "manager". Celui qui va lui dire quand se mettre à jour, et la classe de dispatch.

    Le SRP est peut-être ici violé, mais j'ai du mal à imaginer comment on peut faire autrement dans ce cas précis. Certains aiment bien se dire que si une DrawableEntity est une Entity, et que MovableEntity est une Entity, alors, on ne devrait stocker que des Entity et créer une interface "Update()" au niveau de Entity (et pas au niveau des composants). Sauf que là, on en arrive à devoir faire des downcast pour connaître le type exact (par exemple, draw() n'existe pas chez MovableEntity ). Mais oui, la classe "World", dans ce cas-ci, aura une responsabilité: mettre à jour toutes les Entity. Mais vous en conviendrez que c'est un design erroné.

    Alors bien sûr, on peut dissocier la classe de notification de mise à jours et la classe de gestion de la durée de vie, mais ça ne change rien au problème, vu que de toute manière, la première dépend du second (peut-être pas l'inverse, mais soit).

    Moi, je ne trouve pas que le SRP est violé. A mon avis, la gestion d'une entité et le fait de savoir si la gérer est toujours pertinent va de paire. Ca ne sert donc à rien de vouloir dissocier quelque chose de sémantiquement indissociable, tout ce qu'on gagne, c'est un déplacement de code. Tout ce qu'on perd, c'est la multiplication de classes (littéralement * 2 pour tous les manager). Vu que le manager ne peut pas exister sans son gestionnaire de vie, on écrira systématiquement 2 instructions à chaque création de manager: le gestionnaire de durée de vie, et le manager lui même. Quand on est sûr comme ça que le premier ne peut exister sans l'autre, et que le second n'a pas de service à proposer à qui que ce soit d'autre que le premier, à mon avis il est inutile de les dissocier.

    Et ici, est-ce que Demeter est-il violé ?
    Au sens stricte, oui.
    Mais je trouve que si on réfléchit bien, non.
    Même si la classe Manager va appeller des méthodes (probablement non-const) sur des types d'Entity qu'il n'a pas crée, c'est quand même son but primaire. Il a été conçu pour faire ça. On peut considérer que quelque part, les Entity sont sa responsabilité unique, et qu'elles lui appartiennent.

  16. #76
    Membre expert
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 717
    Points : 3 344
    Points
    3 344
    Par défaut
    Citation Envoyé par Emmanuel Deloget Voir le message
    Personnellement, je vois plus cette classe comme une God Class que comme une classe conteneur. Cette classe est tellement chargée qu'elle est utilisée partout dans le code, je suppose - avec la conséquence que si le code a besoin d'avoir accès à un manager, il va devoir se résoudre à dépendre des autres quand même.

    Le problème de ce design n'est pas encore lié à l'utilisation de la loi de Demeter, mais bien à l'architecture elle-même. Le premier problème, c'est la notion de manager. Si on essaye de répondre à la question "que fait un manager de X", la réponse est invariablement "il gère des X". Gérer ne voulant rien dire (pas de définition précise ; ou plus exactement, tellement de définition qu'on se retrouve avec un petit problème de sémantique...). A la base, un manager est nécessairement une god class, du fait même de son nom. On s'en sert donc partout. Puisqu'on s'en sert partout, la tentation est grande de grouper les managers dans une classe, ce qui transforme celle-ci en hyper-god class, qui est utilisée encore plus partout. A terme : le code devient impossible à maintenir car le moindre changement dans un manager risque d'avoir un impact sur l'ensemble du code.

    Si tu veux voir à quel point World est une god class, commence par encapsuler tous les appels à chacune des fonctions du manager par des méthodes dans World (c'est la base de la loi de Demeter, après tout). TU va rapidement t'apercevoir que le nombre de méthodes explose littéralement.

    Je n'aime pas les manager. Ils sont trop étendus en termes de responsabilités (on en revient toujours à ça dans cette discussion ), sans pour autant définir quel est leur rôle exact. On finit par en avoir tellement marre de leur omniprésence qu'on les transforme en singletons, parce que bon, hein, il ne peut en rester qu'un... Ce n'est pas toujours le cas (parce que des fois, on peut trouver sympathique d'avoir plusieurs managers du même type) mais dans la pratique, ça se termine souvent comme ça quand même.

    Dès lors qu'on commence à se dire "et là, je vais mettre un manager de X", il faut se poser la question de pourquoi. A quoi va servir ce manager ? Quelles seront ses fonctions ? N'y a-t-il pas un découpage plus sensé, qui permettrait très certainement un plus grand découplage ?

    Si on reprends un exemple classique - une "manager de ressources" dans un programme basé sur DirectX, ce manager est une combinaison d'au moins trois fonctions différentes : une fonction de factory, une fonction de collection, et une fonction de contrôle de la durée de vie. Bon nombre d'algorithmes n'ont pas besoin de connaitre la factory et la fonction de contrôle de la durée de vie (par exemple, la récupération sur une erreur device lost). S'il est évident (et encore) que la factory et le contrôle de la durée de vie peuvent s'appuyer sur la collection (l'un pour ajouter des éléments, l'autre pour les supprimer), il n'en reste pas moins que les fonctions ne sont pas liées entre-elles. La fonction de contrôle de la durée de vie est totalement indépendante de la fonction factory, et inversement.

    Bon nombre de managers sont en réalité mieux représentés par plusieurs classes. En ajustant au mieux le découpage d'un manager, on affine aussi son utilisation, qui est nécessairement moins globale que l'utilisation du manager complet. Ce qui permet de réduire le couplage entre les classes. Ce qui permet de ne pas avoir à créer de god class. Ce qui permet de rendre la loi de Demeter plus efficace.
    ?

    Tes assertions sont fausses.

    A vrai dire, je ne vois pas dans ce que j'ai décris ce qui te fais penser a une classe "GodLike". Ce n'est pas un Singleton par exemple, et c'est fait exprès.

    Tout d'abord mon World n'a rien de global, ce n'est qu'un état par instance (parmis tant d'autres possibles), "copiable" et auquel n'accèdent que certaines classes qui le manipulent (en fait 2 dans mon cas). En réalité quasiment toutes les interactions avec l'exterieur se font via des evênements. La manipulation de l'instance elle même se fait par des classes qui representent chacune le type de manipulation qu'on veut faire, soit une "virtual machine" qui fait tourner le World et traduit des requetes exterieures, soit une classe d'edition qui là encore sert de service de manipulation de la dite instance.
    Il n'y a pas qu'un World.
    En fait je ne vois pas ce qui te fait penser que ça serait une "god class" puisqu'encore une fois, ce n'est qu'un état, rien d'autre.

    Cela dit, le nom de manager est aussi vague que tu le décrits. Dans mon cas ce sont bien comme tu dis des gestionnaires, au sens ou ils listent ce qui existe, comprennent une factory (mais n'en sont pas) et s'assurent que les manipulations sont valides. J'aurais pu les appeler plus explicitement mais ce sont en réalité les interfaces publiques d'autres classes qui ont chacune un des roles que je disais au dessus (suivant donc la loi de demeter).

    Aussi, il n'y a rien dans mon exemple qui soit "god class", bien au contraire dans mon cas un World est tout a fait isolé. Même les "règles du jeu" ne s'y trouvent pas, seulement la structure de l'état du jeu.

    J'ai l'impressionn que ce sont certaines pratiques habituelles dans certains développement de jeux qui te font penser ça, aussi peut être qu'utiliser le nom World et parler de jeux fausse ton argumentation?

    J'aurais pu faire le même exemple avec un graph en fait, c'est plus ou moins la même chose, avec juste des règles d'organisation et de creation/destruction des elements contenus maintenus par les dits "manager". Prends plutot World comme une sorte de conteneur a structure spécifique, c'est plus juste.

  17. #77
    Membre expert
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 717
    Points : 3 344
    Points
    3 344
    Par défaut
    En lisant la réponse de JulienDuSud je me rends compte qu'il a peut être été présumé que l'etat du jeu comprends les informations concernant la représentation du jeu. En fait je suis dans le cas inverse. Un peu comme dans les echecs : peu importe comment on représente le jeu, seul les positions sont importantes et on a juste besoin de notifier les changement de position a ce qui s'occupe de l'affichage pour que celui ci représente le jeu correctement.

    Autrement dit, quand je parlais d'état du jeu, c'est bien uniquement l'etat du jeu et rien d'autre. Je sépare tout : règles, etat, représentations (graphiques, audio). Tout est dissocié, entre autre en suivant la règle de role unique par class, mais aussi parceque dans mon cas chacun de ces role peut changer "à la volée". En fait je suis obligé de structuré "au plus flexible", ce qui prends du temps mais a beaucoup d'avantage au final (principalement l'indépendance du code et la facilité a changer du code sans toucher a rien d'autre).

  18. #78
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Premièrement, pas de panique. Je ne fait que dire ce que je pense d'un morceau de code que je vois, sans connaître le contexte de celui-ci et sans non plus présumer de son utilisation. Je ne sais pas si c'est un jeu, un modeleur 3D, un éditeur de monde, ou une application bancaire. A la limite, je n'ai pas à le savoir - le code que tu présente est suffisamment détaché de toute règle métier pour que je ne me pose pas la question. Je réagis donc non pas à ce que tu va faire de ce code, mais bien à la façon dont ce code est conçu.

    Deuxièmement, et histoire de bien me faire comprendre : je ne cherche pas à te mettre dans un position ou tu te sentirais obligé de défendre ton design - c'est le tiens, tu n'a pas à le défendre. J'explique juste ce que j'y vois, et les quelques problèmes qui me sautent aux yeux.

    Enfin, troisièmement : je ne prétends pas avoir la science infuse - je peux me tromper. Ceci dit, je reste convaincu que dans ce cas particulier mon argumentation (car non, ce ne sont pas des assertions) n'est pas, contrairement à ce que tu dis, si fausse que ça.

    Citation Envoyé par Klaim Voir le message
    ?

    Tes assertions sont fausses.

    A vrai dire, je ne vois pas dans ce que j'ai décris ce qui te fais penser a une classe "GodLike". Ce n'est pas un Singleton par exemple, et c'est fait exprès.
    Supposons que je décide d'appliquer à fond la loi de Demeter sur tes différents managers. Comme tu le dit toi même plus loin, ces managers "ce sont bien comme tu dis des gestionnaires, au sens ou ils listent ce qui existe, comprennent une factory (mais n'en sont pas) et s'assurent que les manipulations sont valides.". Ca fait pas mal de travail pour une seule classe. Si chaque comportement est correctement encapsulé, je peux parier que tu aura une petite dizaine de fonction pour chaque manager.

    Ensuite, on continue d'appliquer cette encapsulation à ta classe World (rappel : pas d'accès aux managers depuis l'extérieur). Pour chaque manager, tu as une petite dizaine de fonction. Puisqu'il y a plusieurs manager, tu te retrouve avec plusieurs dizaines de fonctions dans ta classe.

    La conclusion s'impose d'elle même : lorsque tu fait le choix de transformer la classe World pour fournir les services qui sont à l'heure actuelle fournis par les managers qui sont stockés dans la classe World, le nombre de méthodes de World explose, ce qui est un signe assez visible que World se comporte comme une god class (ce qui ne veut pas dire que la classe fait tout, mais qu'elle est un point d'accès central à beaucoup trop de chose). Bien évidemment, puisque tu te permet de passer outre l'encapsulation (tu n'encapsule même pas les données, alors pour les comportements, on est encore loin du compte), tu te caches cet état de fait. Il n'en reste pas moins que ça reste vrai.

    Citation Envoyé par Klaim Voir le message
    Tout d'abord mon World n'a rien de global, ce n'est qu'un état par instance (parmis tant d'autres possibles), "copiable" et auquel n'accèdent que certaines classes qui le manipulent (en fait 2 dans mon cas).
    Question : si seules deux classes accèdent à World, pourquoi World est-elle aussi fournie en managers ? Dans ma vision peut être un peu étriquée de l'architecture logicielle, si deux classes A et B dépendent d'une classe C, alors le nombre de services offerts par C est relativement limité. Ce n'est pas tellement le cas dans ton design, donc je me demande si ces deux classes dont tu parles ne font pas beaucoup trop de choses... Mais bon, sans les voir, je ne peux pas juger.

    Citation Envoyé par Klaim Voir le message
    En réalité quasiment toutes les interactions avec l'exterieur se font via des evênements. La manipulation de l'instance elle même se fait par des classes qui representent chacune le type de manipulation qu'on veut faire, soit une "virtual machine" qui fait tourner le World et traduit des requetes exterieures, soit une classe d'edition qui là encore sert de service de manipulation de la dite instance.
    C'est très bien - et cela repose encore la question de "pourquoi tant de services dans World, si on n'accède pas à ces services à partir de World ?

    Citation Envoyé par Klaim Voir le message
    Il n'y a pas qu'un World.

    En fait je ne vois pas ce qui te fait penser que ça serait une "god class" puisqu'encore une fois, ce n'est qu'un état, rien d'autre.
    Un état, en programmation objet, ça n'existe pas. Un état est forcément intrinsèque à un objet. Un état qui n'est pas intrinsèque à un objet signifie probablement qu'il n'a pas à être là, ou qu'il manque une abstraction quelque part.

    De plus, si c'était effectivement un état, comment se fait-il qu'il fournisse des services, par le biais des différents managers ? Un état ne fournit pas de services - un objet, qui possède un état et a pour mission de le maintenir, le fait.

    Je reprends mon argumentation un peu plus tard - là, il faut que j'y aille. A tout à l'heure
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  19. #79
    Membre confirmé Avatar de Lavock
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    560
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 560
    Points : 633
    Points
    633
    Par défaut
    Citation Envoyé par Emmanuel Deloget Voir le message
    Un état, en programmation objet, ça n'existe pas. Un état est forcément intrinsèque à un objet. Un état qui n'est pas intrinsèque à un objet signifie probablement qu'il n'a pas à être là, ou qu'il manque une abstraction quelque part.
    Hum, c'est un peu hors sujet, mais que veux tu dires par là ?
    Pour des raisons d'optimisation et de respect du LSP, généralement je considère des états. L'exemple type est le calcul d'une intersection entre une droite et une quadrique : je perd énormément de temps si je ne considère pas l'état "sphère".
    The mark of the immature man is that he wants to die nobly for a cause, while the mark of the mature man is that he wants to live humbly for one.
    --Wilhelm Stekel

  20. #80
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 081
    Points
    16 081
    Par défaut
    Citation Envoyé par Lavock Voir le message
    Hum, c'est un peu hors sujet, mais que veux tu dires par là ?
    Pour des raisons d'optimisation et de respect du LSP, généralement je considère des états. L'exemple type est le calcul d'une intersection entre une droite et une quadrique : je perd énormément de temps si je ne considère pas l'état "sphère".
    Par définition (dans le paradigme OO), l'état d'un objet est la valeur de ses attributs à un instant donné.

    Pour faire simple, ce sont les valeurs qu'il faudrait "sauvegarder" si on voulait persister cette instance d'objet (par exemple dans une bdd) afin de pouvoir la "restaurer" plus tard.
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

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, 10h17
  2. Que pensez vous de filemaker
    Par thpopeye dans le forum Autres SGBD
    Réponses: 4
    Dernier message: 14/06/2007, 15h20
  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, 15h45
  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, 12h12
  5. Que pensez vous du mariage ASP Flash?
    Par tyma dans le forum Flash
    Réponses: 4
    Dernier message: 09/07/2003, 15h00

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