Le principe de demeter n'indique pas qu'il faille mettre tout à plat pour éviter de le violer. Mettre tout à plat en ajoutant 1000&1 méthodes pour ne pas le violer, c'est tout simplement mal corriger un problème de conception. En général, si tu commences à trop enfreindre cette loi ou que ta classe a trop de méthodes pour éviter de l'enfreindre, c'est probablement qu'il y a un problème de conception et que le niveau d'abstraction n'est pas le bon.
Ressources proposées par 3DArchi - Les fonctions virtuelles en C++ - Cours et tutoriels C++ - FAQ C++ - Forum C++.
Là, tu joues sur les mots. Elle dit effectivement que tout DOIT être à plat ! (bon, elle dit pas que ça, mais c'est sur ce point que je critique).
Attention à ne pas partir dans trop d'extrémisme. Je sais, je suis le premier à avoir parler de 40 inlines (oO), mais tâchons de rester sobre :
-> Une interface (ou un objet) qui, d'un point de vue externe, sont insécable et avec uniquement que des méthodes utiles.
Reste le problème soulevé toujours présent... C'est une question de "Bon sens" (TheNOHDirector).
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
A aucun moment on parle de mettre quoi que ce soit à plat. Je maintient, comprendre : la loi implique qu'il faut mettre à plat pour pouvoir accéder aux objets c'est faire fi de l'ensapsulation et l'abstraction. Quitte à me répéter, c'est se tromper sur la façon dont résoudre le problème. Si une méthode a besoin d'appeler une méthode d'un objet obtenu via un autre, il y a des risques que l'objet expose des détails d'implémentation ou que son niveau d'abstraction ne soit pas le bon.Envoyé par Loi Demeter
Ressources proposées par 3DArchi - Les fonctions virtuelles en C++ - Cours et tutoriels C++ - FAQ C++ - Forum C++.
--> Oui tant que c++03. Cf un de mes posts précedent.
C'est un autre problème. Je ne considère pas se cas là (dans se cas, il y quelque chose à faire pour se rapprocher de Demeter).ou que son niveau d'abstraction ne soit pas le bon.
La loi dit clairement qu'on ne doit pas accéder au méthode d'un retour de méthode (généralement getters). Donc on a le droit qu'a un seul niveau d'accès en profondeur (<= mise à plat). Peut importe le comment de la mise à plat d'ailleurs.
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
Ressources proposées par 3DArchi - Les fonctions virtuelles en C++ - Cours et tutoriels C++ - FAQ C++ - Forum C++.
+1 pour le "petit voyant", sans parler des setters.
"Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu
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
Oui d'une manière générale les getter/setter ne sont plus trop recommandés, car ils brisent l'encapsulation. Mais là encore il faut savoir faire la part des choses, est-ce qu'on parle d'objet du domaine métier (avec leur intelligence) ou est-ce qu'on parle d'objet POJO (ou PODS en C++).
Dans les deux cas la conception et le design des POJO autant que des objets métier. Après vient encore la maintenance du code, qui complique encore la tache, ou on peut se retrouver avec du code qui est loin de la loi de Demeter.
Je suis également d'accord avec 3DArchi, à un certain moment les problèmes rencontrés révèle très certainement un problème de design. Dans l'absolu j'essaye de suivre les bons principes.
Petit article à lire : http://butunclebob.com/ArticleS.Uncl...rinciplesOfOod
Kent Beck a aussi écrit des bouquins intéressants.
Faire du TDD peut aider à faire du code maitrisé, focalisé, maintenanble et bien sur testé, mais ça ne fait pas tout.
Encore une fois il faut du bon sens, comme partout!
Marrant... Je travaille sur quelque chose d'assez similaire, ces temps ci, et je suis une approche assez similaire, même si mes structures ne sont pas un décalque parfait du xml (au niveau le plus bas, je ne fais pas de structures mais des champs, directement).
Pour les fonctions communes (lecture, écriture des xml, et traitements de base), j'utilise des surcharges d'opérateurs, ou des fonctions homonymes, ce qui permet d'éviter de cribler le code de if() et de switch(). C'est l'amélioration que je te conseillerais. Il faut juste les documenter précisément.
Certes, ce ne sont pas des "bonnes pratique", mais il en résulte un code extrèmement facile à maintenir, parce qu'il suit de très près la structure du fichier. D'une certaine manière, la spec de ton xml est la documentation de ton programme. Par ailleurs, si un bout de la spec xml change, je sais immédiatement où regarder dans mon code, et je sais que tout mainteneur futur y arrivera également.
Le programme résultat est tout à fait inélégant, et contraire au principe selon lequel il faut éviter les redondances de code (chez moi, il y en a partout, les plus évidentes sont dans des fonctions statiques externes, mais il en reste beaucoup). Mais on échange un code compact contre un code facile à lire, et plus je vieillis, plus je me dis que c'est souvent une bonne idée...
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it" (déboguer est deux fois plus difficile qu'écrire le code d'origine, aussi, si vous écrivez le code le plus intelligent qui soit, vous n'êtes, par définition, pas assez malin pour le déboguer), comme disait l'autre...
Francois
Dernière modification par Invité ; 16/10/2009 à 21h56.
Ton 7 +/-2 est correcte, certes, mais tu parles toi-même d'éléments. Pas de caractères. 5 à 7 paramètres, on s'en souvient vaguement. 5 à 7 mots dans un identifiants, on s'en souviendra aussi bien - et ça commence à faire un pétard de long identifiant
Oui...
Non
Le SRP ne dit pas de partitionner les classes (même si dans la pratique, ça reviens vaguement à ça) ; il dit de s'assurer qu'une classe ne possède qu'une seule responsabilité. Demeter ne dit pas "faite comme si rien n'était partitionné", elle dit "encapsulez les traitements afin de ne pas avoir besoin de désencapsuler l'accès aux données". Les données étant intrinsèques à une classe, il n'est pas normal de pouvoir les manipuler de l'extérieur.
Dans la pratique, le SRP et la loi de Demeter ne sont pas contradictoire - bien au contraire. Les deux proposent une approche complémentaire de l'encapsulation visant à s'assurer d'une part que le code sera lisible et d'autre part que le code sera bien construit.
Si j'ai bien compris les rrefs, il ne s'agit pas là d'une des utilisations envisagées - en tout cas, pas comme moyen de contourner l'encapsulation.
Dans la pratique, cet être horrible cesse d'émettre la moindre idée dès lors qu'il entend des mots qu'il ne comprends pas. Et s'il comprends tous les mots compliqués que tu va lui sortir, alors il se pourrait qu'il ne soit pas si horrible que ça et que ses conseils aient un sens
... et puisque tu n'avais pas à l'époque les connaissances suffisantes pour lui assurer que ce n'était pas la bonne solution, tu as été obligé d'obéir. Ne t'inquiète pas : c'est le métier qui rentre...
Etonament, tu n'es pas la seule personne à t'être posé la question Plus sérieusement, de nombreux programmes manipulent des données XML, et globalement :
* soit le XML est pensé correctement, et dans ce cas la solution s'impose presque d'elle même : chaque tag est un objet, avec ses méthode et ses données. Il n'y a pas de raison pour que les données soient publiques ou même qu'elles soient exposées avec des mutateurs quelconques.
* soir le XML est bancal, et il devient difficile de construire une abstraction à partir de ce dernier. Le traitement doit se faire en deux phases :
1) lecture du XML et stockage des données
2) construction d'un arbre d'objets correctement architecturé à partir de ces données.
Notons que le problème est le même pour toute autre représentation (base de donnée, fichier binaire, ...). Le fait de récupérer des données dans une base de donnée (au sens large : fichier, SGBD, ...) n'impose pas nécessairement une architecture logicielle particulière. Je dirais même plus : dans de nombreux cas, ça n'est pas souhaitable.
Ce n'est pas vraiment un problème de sémantique mais bien (comme je l'ai dis avant) de taxonomie.
* un principe doit être respecté - c'est une base de travail. Ne pas respecter un principe conduit à construire une architecture qui sera encombrée de nombreux problèmes et qui rendra la maintenance du produit difficile et couteuse. Les grands principes de deisgn orienté objet sont un peu les axiomes de l'architecture.
* de ces principes, on tire des corolaires. Les corolaires sont un peu les théorèmes de notre métier. Ils nous disent "si tu fait ça, il va se passer ça".
* en dehors des principes et des corolaires, il y a les lois, qui se rangent en plusieurs catégories :
- les lois d'architecture, qui proposent des solutions permettant d'améliorer l'architecture logicielle d'un produit
- les lois de style (comme la loi de Demeter) qui se proposent d'améliorer la qualité du code produit
- les lois de processus (par exemple les "principes" d'XP) qui se proposent de définir un cadre pour effectuer notre travail (par exemple la programmation par paire).
Appliquer les lois supposent qu'on a établi un contexte dans lequel on peut les appliquer. En ce qui concerne les lois d'architecture, ce contexte est créé en appliquant les principes fondamentaux (OCP, LSP, SRP, DIP, ...). Le contexte nécessaire à l'application des lois de style est l'écriture du code, qui ne peut se faire que si une architecture a été devisée. Enfin, les lois de processus sont applicables dès lors que la notion de projet a été posée.
Donc on va rester avec le terme "loi de Demeter", parce que c'est bel et bien une loi et non pas un principe.
[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.
Je réserve pour l'instant mon jugement sur Demeter, je n'ai pas assez de retour d'expérience dessus pour juger, d'où mon silence sur ce thread que je lis attentivement.
Mais ici je suis un peu surpris par la version que tu en donnes. Tu parles de désencapsuler l'accès aux données. Tel que j'avais cette loi en tête jusqu'à présent, elle me semblait plus stricte. C'est à dire que monObjet.traitement1().traitement2() était interdit par cette loi, et pas uniquement monObjet.getData().traitement().
La différence peut sembler minime, mais elle me semble significative, car dans cette version "assouplie", Demeter peut n'être qu'une autre version de "éviter les getters/setters, ils brisent généralement l'encapsulation".
Qu'en est-il ?
Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.
En fait, tout dépend du type renvoyé par traitement1()...
Il n'a jamais été dit qu'aucune fonction membre ne devait renvoyer de valeur!!!
Il a juste été dit que les valeurs de retour sont restreintes et peuvent peu ou prou entrer dans:Si donc traitement1() renvoie une référence sur (une copie de) l'objet en cours, cela ne me choque absolument pas...
- Les types primitifs ou assimilables (je pense par exemple à std::string comme type assimilable), en veillant cependant, dans le cas de std::string à en renvoyer soit une copie soit une référence constante.
- L'objet lui-même (ou une copie de l'objet lui-même)
Par contre, si traitement1() agit, pour faire simple, comme un getter "amélioré" et renvoie un membre de l'objet, cela me semble déjà beaucoup plus choquant.
Le tout modulo, bien évidemment, le fait que l'objet agisse en réalité comme... une collection
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
Il faudrait que j'aille voir les sources originales. L'idée de fond est peut-être intéressante, mais les présentations que j'ai vues me semble juste pousser à écrire des wrappers inutiles.
Comme on ne peut pas faire
on va écrire
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 void C::f(A a) { a.g().h(); }
avec Bh dupliqué à un tas d'endroit. A moins qu'on le pousse dans A où il sera implémenté
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 void C::Bh(B b) { b.h(); } void C::f(A a) { Bh(a.g()); }
ce qui ne me semble pas nécessairement mieux. Mais ça dépend pas mal des rapports entre les classes.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 void A::Bh(B b) { b.h(); } void A::Bh() { Bh(g()); }
Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.
Bonsoir,
Personnellement, j'ai l'impression que quand on fait quelque chose comme l'exemple que tu montres, on tombe un peu dans le travers de vouloir coller à un principe dans l'implémentation plutôt que de l'intégrer dans la conception. En d'autres termes, on demande à f(a) d'en savoir trop sur a pour réaliser son service.
Ressources proposées par 3DArchi - Les fonctions virtuelles en C++ - Cours et tutoriels C++ - FAQ C++ - Forum C++.
En fait je dirais plus qu'au lieu de faire :
Puis :
Code : Sélectionner tout - Visualiser dans une fenêtre à part C::C( A a ) : a_( a ) {}
On ferait :
Code : Sélectionner tout - Visualiser dans une fenêtre à part C::f() { a_.g().h(); }
Et directement :
Code : Sélectionner tout - Visualiser dans une fenêtre à part C::C( B b ) : b_( b ) {}
Ok, ça c'est dans le cas où C n'a par ailleurs pas besoin de l'instance de A, mais même si c'était le cas :
Code : Sélectionner tout - Visualiser dans une fenêtre à part C::f() { b_.h(); }
Le fait que A ait aussi besoin d'une instance de B concerne A uniquement, et pas C.
Code : Sélectionner tout - Visualiser dans une fenêtre à part C::C( A a, B b ) : a_( a ), b_( b ) {}
Si une classe a besoin d'une instance d'une autre pour fonctionner, on lui fournit directement plutôt que d'aller la récupérer par des moyens détournés ailleurs.
MAT.
Comme je l'ai écrit, il faudrait que j'aille voir à la source quel est l'objectif. Ce qui est vraisemblable, c'est qu'il est trop facile de respecter la formulation syntaxique sans respecter l'objectif.
J'écris beaucoup de code qui ne respecte pas ce principe. Cela tient peut-être à la nature des rapports entre les classes, mais tous les moyens que je vois pour le respecter me semblent artificiels (la seule classe de solution que je n'ai pas réellement envisagées, c'est de modifier les interfaces des classes dont je suis le client).
Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.
Salut,
C'est un peu ce que je voulais dire. Si on se contente d'artifices pour respecter par la syntaxe ce principe, alors on va probablement pas bénéficier des objectifs à la base (qui sont essentiellement moins de couplage et une indépendance sur l'implémentation d'une classe).
En réfléchissant, je me suis rendu compte qu'il y avait aussi des cas où je ne respectait pas ce principe, souvent sur de l'I.H.M. Je peux avoir par exemple dans une boîte dialogue à récupérer un contrôle et à invoquer une méthode sur ce contrôle. Mais j'ai tendance à penser que si syntaxiquement un contrôle n'est pas membre de la boîte de dialogue, d'une certaine façon sémantiquement, si. J'imagine qu'il peut y avoir d'autres cas similaires.
Ressources proposées par 3DArchi - Les fonctions virtuelles en C++ - Cours et tutoriels C++ - FAQ C++ - Forum C++.
Ce qui est étonnant, c'est qu'on a eu une discussion de 3 pages presque sans aborder ce point qui est pourtant fondamental. Faire le point, ça peut aussi être une bonne manière de faire avancer le débat.
Dans sa définition, Demeter étant une loi de style, il semblerait qu'on puisse l'appliquer partout. Ce n'est évidemment pas le cas, à moins de recourir à des artifices plutôt... artificiels. Demeter nécessite donc de faire des choix d'architecture logicielle qui vont amener à son application naturelle. En gros : ce n'est pas l'application de la loi de Demeter qui rendra le code meilleur, c'est le fait qu'on puisse l'utiliser et qu'on le fasse qui nous intéresse vraiment.
Ceux qui veulent vraiment voir l'effet que ça peut avoir sur le code devraient se forcer à créer un petit projet orienté objet avec les caractéristiques suivantes (sans même forcément appliquer la loi de Demeter):
* pas de référence circulaire dans le graphe des classes.
* toutes les données sont privées. Pas protected. Pas public. Privées.
* pas de mutateurs du tout ; on encapsule les traitements, pas les données.
Bien sûr, les principes de base (OCP, LSP, SRP, DIP, ISP) doivent aussi être respectés.
Une fois que vous avez le code, faite une passe de refactoring pour appliquer la loi de Demeter - normalement, son application devrait être naturelle dans ce cas. En fait, vous devriez voir que le nombre de places ou vous aurez à changer le code est très restreint - tout simplement parce que votre design est déjà très Demeter-friendly.
[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.
C'est peut-etre lie a mon domaine d'application, mais ca me semblerait extremement artificiel. Il y a des classes qui sont intrinsequement liees et vouloir les decoupler n'a absolument aucun sens.
Je me demande si la cause de ma gene ne serait pas que, dans les domaines ou j'ai travaille, la classe est un niveau de decoupage trop fin pour ce genre de regles. (Autrement dit, je crois comprendre l'objectif, il me semble louable, mais le niveau me semble inadapte).
Aucun probleme avec ca.* toutes les données sont privées. Pas protected. Pas public. Privées.
Globablement d'accord mais la limite est parfois ambigue. Si tu refuses setPosition() mais que tu admets moveTo() (le deux faisant exactement la meme chose), ca ne me gene vraisemblablement pas.* pas de mutateurs du tout ; on encapsule les traitements, pas les données.
Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager