Débat sur un principe de POO : la Loi de Demeter
. Quelles méthodes peut appeler un objet ?

Le , par 3DArchi, Rédacteur
Bonjour,
Ceci fait suite à cette discussion .
On peut trouver une définition assez complète de la loi de Demeter dans ce billet d'Emmanuel Deloget.
En résumant, l'idée de la loi est de dire que dans une méthode m d'une classe A, je ne peux utiliser que :
-> les méthodes de A (et ses classes de bases) ;
-> les méthodes des membres de A ;
-> les méthodes des paramètres de m ;
-> les méthodes des objets créés par m ;
-> les méthodes des variables globales.
En revanche, cette loi sous-tend qu'on n'a pas de transition : on ne devrait pas utiliser :
-> les méthodes des membres (ou des objets retournés par) des membres de A ;
-> les méthodes des membres (ou des objets retournés par) les paramètres de m.
etc.
L'idée est qu'à travers cette loi, un objet masque à son utilisateur son contenu.
Comme le note le billet, cette règle souffre d'exception : les structures triviales, les conteneurs et toutes sortes de fabriques pour des raisons évidentes.

Pour reprendre les termes du débat :
Citation Envoyé par JulienDuSud  Voir le message
cette loi est bien trop restrictive et amène souvent à de la redondance de code au niveau de la classe passerelle vis à vis de la classe source. D'ailleurs, en pratique cela donne trop de responsabilités à une classe.

Souvent, quand cette loi n'est pas respectée, c'est que soit on expose des détails d'implémentation, soit on n'a pas le bon niveau d'abstraction. Et quand on n'a pas le bon niveau d'abstraction, on a l'impression que pour respecter la loi il faut transposer tout ce qui a dans la classe source à l'intérieur de la classe passerelle, ce que à quoi toutes nos fibres de C++eurs s'insurgent.

Pensez-vous respecter cette loi ? La trouvez-vous trop contraignante ? Quand pensez-vous devoir la respecter et quand la violer ? Bref, quel est votre sentiment ?


Vous avez aimé cette actualité ? Alors partagez-la avec vos amis en cliquant sur les boutons ci-dessous :


 Poster une réponse

Avatar de koala01 koala01 - Expert éminent sénior http://www.developpez.com
le 28/01/2016 à 19:35
Citation Envoyé par igor_  Voir le message
- C'est mon exemple, donc j'en fixe les données, c'est normal.

Oui, et les données que tu fixes à ce sujet sont -- en gros -- "Rien à foutre de l'ISP"...
- Si tu refuses l'hypothèse de travail, tu passes à autre chose.

Oui, parce que, à partir du moment où tu décide expressément de ne pas respecter les fondamentaux de la conception, au mieux, tu fonces dans un mur, au pire, ton travail perd toutte crédibilité!
- Mais si tu veux argumenter sur l'exemple principal de mon article, il faut en accepter les hypothèses.

C'est ce que j'ai fait dans ma dernière intervention... Du moins, aussi longtemps que possible. Car, une fois qu'il est démontré que l'hypothèse est fausse, tout le raisonnement tombe à l'eau

Chacune montre clairement que je pense à un postulat, donc pas de démonstration.

Oui, mais un postulat n'a besoin d'aucune preuve : on croit en Dieu, quel que soit le nom qu'on lui donne, ou non... Personne ne sera capable d'affirmer ou d'infirmer ce postulat.

Le développement informatique ne peut pas se contenter de postulat, car il ne connait que "vrai" et "faux", sans aucune place pour le "peut-être". Si bien que toute hypothèse formulée à ce sujet doit pouvoir être démontrée logiquement, "mathématiquement" dirais-je
Et soit dit en passant, ce postulat est que les types utilisés comme composantes internes sont également utilisés par le client par ailleurs.

Ca, c'est peut être la seule hypothèse démontrable de ton raisonnement. Car, effectivement, les types utilisés comme composantes internes sont régulièrement utilisées seule "par ailleurs"...
Ensuite tu compliques vraiment les choses : le raisonnement derrière le nombre de services à répliquer est très simple :

Ben, tu pars dans une démonstration par l'absurde, en refusant une évidence : ton hypothèse est caduque. Je ne fais que pousser le raisonnement à l'extrême
Je compare formellement 2 conceptions : l'une qui permet de respecter Déméter, l'autre non. Evidemment, pour que la comparaison de leurs propriétés respectives ait un sens, le nombre de services doit être le même.

Pourquoi une classe ne doit exposer que les services strictement nécessaires à son utilisation, et cela te permettra de respecter Déméter.

Tu peux très bien avoir une classe avec trois accesseurs (sur des données internes) qui ne respecte absolument pas Déméter et la même classe, exposant cinq services que l'on est effectivement en droit d'attendre de la part de la classe qui respectera scrupuleusement Déméter, simplement parce que toute notion de donnée interne sont absentes de son interface
Si c'est ce que tu veux dire, je ne considère pas le cas où Comportement, par exemple, aurait 5 méthodes publiques utiles à l'implémentation de Agent mais non nécessaires à l'utilisateur de Agent.

Et pourtant, c'est pour ainsi dire le seul cas qui mérite d'être considéré!!!

Car il faut bien comprendre que si tu as décidé d'utiliser une donnée de type Comportement dans ta classe Agent, c'est quand même pour une bonne raison : ta classe Comportement est capable de fournir des services qui aideront ta classe Agent dans sa tâche. Si elle n'avait pas eu cet intérêt majeur, tu aurais tout aussi bien pu "dupliquer" toutes les données privées de Comportement dans ta classe Agent et les manipuler "tout simplement" (en dupliquant sans doute au passage toute la logique mise en place par les différents services offerts par Comportement)
Dans un tel cas, la version avec Déméter ne répliquerait naturellement pas ces 5 services, mais la version sans Déméter pourrait alors tout aussi bien ne pas exposer un Comportement entier mais une interface où ces 5 services sont absents !

Ben oui, bien sur!!! Et en échange, elle peut exposer deux, trois, cinq ... quinze (meme, si tu veux) services qui utilisent comportements mais pour lesquels l'utilisateur de ta classe Agent n'a absolument aucun besoin de savoir qu'il manipule une donnée de type Comportement "en arrière plan".
Tu fais fausse route. Dans cet exemple, j'inverse la démonstration. Complètement.
Je pars donc d'une classe Agent pour laquelle j'ai défini 7 services qui m'intéressent dans un premier temps. Et pour implémenter ces services, j'en suis venu à définir 2 classes internes. Comme ces classes internes se sont révélées bien correspondre à des concepts du domaine métier, j'ai décidé de soigner leur interface et d'en faire des composants réutilisables ailleurs que dans Agent.
Et me voilà avec deux composantes de Agent : Comportement et Savoir.

Et tu as raison d'agir de la sorte...

Jusqu'au moment où tu décides de te mettre dans une situation dans laquelle tu dois connaitre Comportement et Savoir pour pouvoir utiliser ta classe Agent... Ca brise l'encapsulation, ca birse l'ISP, et ca brise Déméter (et ca la fout mal pour l'OCP, entre autres-
Je te lis depuis qq jours, koala01, et tu ne cesses de tenir des propos déplacés, de sortir de grands mots, comme si tu avais un cours à donner. Et d'un autre côté, je constate que tu es à côté de la plaque presque tout le temps. C'est bizarre, non ?

Tu m'accorde à vrai dire la crédibilité que tu veux, bien sur... Tu as l'impression que je "pête plus haut que mon cul" et que "je ne sais absolument pas de quoi je parle" libre à toi de le croire

Mais observe un tout petit peu mes statistiques sur le forum et penses quand même au fait que, pour avoir à peu près le double de points que d'interventions, j'ai quand même du avoir un sacré nombre de votes positifs, et que je ne les ai pas obtenus en n'intervenant que sur des questions à débat, très loin de là

Penses aussi que je suis (eh oui, j'en suis particulièrement fier )l'auteur d'un livre dont l'appréciation sur les différents forums est excellente et qui est référencé au CNES dans la bibliographie pour les prochaines règles qualité en C, C++ et embarqué.

Peut-être serait il donc temps de faire une légère introspection personnelle (promis, si tu fais la tienne, je fais la mienne) et de se demander "qui est à coté de la plaque"... Qu'en penses tu

EDIT : d'ailleurs, au passage!!! Sachant qu'il est impossible de voter pour ou contre ses propres interventions (et que je n'ai donc pas "truqué" les votes), tu pourras te faire une idée de qui est le plus crédible en comparant le nombre de votes positifs que j'ai recu depuis que tu as déterré le sujet avec le nombre de vote négatifs que tu as obtenu dans le même laps de temps... Cela ne te donne vraiment pas envie de reconsidérer ton approche
Avatar de koala01 koala01 - Expert éminent sénior http://www.developpez.com
le 28/01/2016 à 19:59
Citation Envoyé par igor_  Voir le message
>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).
est l'existence de contre-exemples à Déméter qui ne fasse pas partie des cas d'exception connus.
Autrement-dit, si tu te trouves dans de tels cas et que tu appliques Déméter (logique car pas un cas d'exception), tu risques de déteriorer le design.

Cela correspond bien à l'affirmation, non ?

A vrai dire (mais bon, je suis peut être un tout petit peu extrémiste sur le sujet, je te l'accorde), il n'y a pas d'exception qui tienne réellement la route que ce soit pour Déméter ou pour SOLID.

Je penses en effet de plus en plus que, lorsque l'on se trouve dans une situation dans laquelle il faut "sortir l'exception" relative à l'un de ces principes (ou à cette loi), c'est qu'il y a un problème de conception qui nous a déjà placé dans cette situation, et que l'exception que l'on décidera d'accepter n'aura pour seul but que de contourner les conséquence du problème de conception précédant et pour seule conséquence... que de nous obliger à accepter d'autres exceptions par la suite.

En un mot comme en cent : quand tu commence à avoir besoin de faire appel à une exception pour Déméter ou pour SOLID, tu commences à construire un mur dans lequel tu finiras tôt ou tard par t'emplafonner brutalement

Mais bon, je le redis et je l'avoue sans honte j'ai tendance à être extrémiste sur le sujet
Avatar de mister3957 mister3957 - Membre éprouvé http://www.developpez.com
le 28/01/2016 à 20:30
La loi de Déméter est un principe fondamental. Alors certes ça peut être contraignant au début, on peut ne pas vraiment comprendre pourquoi "faire du code" supplémentaire alors que ça peut très bien "marcher" sans.. mais quid en terme de qualité, de cohésion et de fonctionnel à moyen et long termes ?

Dans l'article il est écrit "Prenons l’exemple plus neutre de la prise de rendez-vous avec un médecin". Voilà le style de raccourcit qu'interdit la loi de Déméter, personne ne prend rendez-vous avec un médecin, mais avec un cabinet dont le fait qu'il y soit (encore) médecin n'est qu'une supposition quant à la composante de son implémentation, de son organisation de la structure avec laquelle on souhaite prendre rendez-vous.

Et si c'est le médecin directement, ça passera toujours par une structure, certes unipersonnelle mais structure quand même. Et si ça le fait chier de recevoir les appels alors qu'il a une secrétaire pour ça, alors pourquoi ce con a-t-il redirigé le numéro de la structure vers son portable ? Ou pourquoi c'est le téléphone dans son local qui sonne et non pas celui de la secrétaire ? En tout cas ça veut dire que son organisation, son implémentation n'est pas correcte, pas évolutive et il y a carrément un problème de conception qui n'a rien à voir avec celui qui prend rendez-vous (l'utilisateur du service supposé).

Demain c'est mon tour d'amener les petits pains, j'ai pris commande non pas avec le boulanger, mais avec la boulangerie..

Pour l'exemple de chien.getPattes().cours(), ça peut "marcher", ou plutôt "courir" (ha ha ha que je suis drôle, pardonnez moi je suis fatigué) mais si demain ça demande à ce qu'en prérequis le cerveau doive sortir de son sommeil, localiser le bâton après quoi courir, la mâchoire pour l'attraper, le cerveau à nouveau pour localiser son maître afin de le rapporter, le manière la plus pragmatique est de coupler les pattes avec le cerveau et la mâchoire en passant par le chien, bref, tout est couplé et on ne peut plus réutiliser l'ingénierie des pattes sur un chat ou un lapin.

Et c'est là où commence l'effet "plat de spaghetti", une des pires erreurs bien que facile à faire et pourtant tellement simple à éviter lorsque l'on fait jouer la cohérence..

"Low coupling, high cohesion"

Après peut-être que dans les langages interprétés ça peut faire un gain de performances exécutives, ça je ne sais pas, mais ça ne change rien à ce niveau là pour les langages compilés
Avatar de igor_ igor_ - Nouveau Candidat au Club http://www.developpez.com
le 29/01/2016 à 15:03
Citation Envoyé par mister3957  Voir le message
La loi de Déméter est un principe fondamental. Alors certes ça peut être contraignant au début, on peut ne pas vraiment comprendre pourquoi "faire du code" supplémentaire alors que ça peut très bien "marcher" sans.. mais quid en terme de qualité, de cohésion et de fonctionnel à moyen et long termes ?

Dans l'article il est écrit "Prenons l’exemple plus neutre de la prise de rendez-vous avec un médecin". Voilà le style de raccourcit qu'interdit la loi de Déméter, personne ne prend rendez-vous avec un médecin, mais avec un cabinet dont le fait qu'il y soit (encore) médecin n'est qu'une supposition quant à la composante de son implémentation, de son organisation de la structure avec laquelle on souhaite prendre rendez-vous.

Et si c'est le médecin directement, ça passera toujours par une structure, certes unipersonnelle mais structure quand même. Et si ça le fait chier de recevoir les appels alors qu'il a une secrétaire pour ça, alors pourquoi ce con a-t-il redirigé le numéro de la structure vers son portable ? Ou pourquoi c'est le téléphone dans son local qui sonne et non pas celui de la secrétaire ? En tout cas ça veut dire que son organisation, son implémentation n'est pas correcte, pas évolutive et il y a carrément un problème de conception qui n'a rien à voir avec celui qui prend rendez-vous (l'utilisateur du service supposé).

Demain c'est mon tour d'amener les petits pains, j'ai pris commande non pas avec le boulanger, mais avec la boulangerie..

Pour l'exemple de chien.getPattes().cours(), ça peut "marcher", ou plutôt "courir" (ha ha ha que je suis drôle, pardonnez moi je suis fatigué) mais si demain ça demande à ce qu'en prérequis le cerveau doive sortir de son sommeil, localiser le bâton après quoi courir, la mâchoire pour l'attraper, le cerveau à nouveau pour localiser son maître afin de le rapporter, le manière la plus pragmatique est de coupler les pattes avec le cerveau et la mâchoire en passant par le chien, bref, tout est couplé et on ne peut plus réutiliser l'ingénierie des pattes sur un chat ou un lapin.

Et c'est là où commence l'effet "plat de spaghetti", une des pires erreurs bien que facile à faire et pourtant tellement simple à éviter lorsque l'on fait jouer la cohérence..

"Low coupling, high cohesion"

Après peut-être que dans les langages interprétés ça peut faire un gain de performances exécutives, ça je ne sais pas, mais ça ne change rien à ce niveau là pour les langages compilés


Si un médecin libéral travaillant avec une secrétaire vous fait la commande d'un logiciel permettant de modéliser et tracer les interactions entre lui, sa secrétaire et le patient lors de la prise de RV, on peut imaginer 2 cas de figure :
- vous faites ce qu'il vous a demandé et vous serez payé.
- vous estimez que, dans le besoin exprimé, seul compte vraiment le point de vue du patient et qu'il faut abstraire le reste. Vous créer donc une interface abstraite offrant au patient tous les services nécessaires à sa prise de rendez-vous, et vous implémentez ces services de manière interne. Vous êtes plutôt fier de votre conception, car vous avez su aller au-delà du besoin exprimé. Lorsque vous montrez votre prototype au médecin, vous commencez par lui vanter les capacités d'adaptation de votre architecture : le patient ne dialoguant qu'avec une interface abstraite (un "cabinet médical"), le logiciel pourra fonctionner avec un ou plusieurs médecins, zéro ou un ou plusieurs secrétaires, et même en l'absence de tout personnel, en prévoyant des comportement par défaut. Tout ce qu'il y aura à faire, c'est d'implémenter de nouveaux comportements. l'interface principale, elle, restera inchangée. Alors le médecin vous répond : "Mais je vous avais pourtant bien dit que ce que nous avions besoin, c'était de modéliser les interactions directes entre moi, ma secrétaire, et le patient. C'est tout-à-fait impossible avec votre application. Vous avez créé un mur qui empêche cela.

mister3957, comme beaucoup d'intervenants, vous vous précipitez pour imaginer des besoins. Et si et si et si. Je ne dis pas que vos généralisations et vos abstractions ne sont pas bonnes. Elles sont excellentes et plutôt naturelles. Mais, simplement, elles sont hors sujet ici. Dans mon article, l'exemple du médecin explicite des entités claires, et il exprime clairement la problématique de la prise de RV en termes d'interactions entre ces entités. Vous pouvez proposer toute autre modélisation, abstraite à souhait, aucun souci. Mais c'est alors un autre cas d'étude. Vous pouvez dire que ma modélisation ne peut correspond à aucun problème en pratique et qu'elle ne sert donc à rien, mais c'est une position difficile à tenir : il est plus facile d'affirmer qu'une modélisation peut correspondre à des cas réels que l'inverse. J'ai tenté de précisé un cas réel plus haut.

Enfin, cet exemple du médecin et avant tout une image. Dans mon article, je ne le détaille pas plus que l'exemple du chien qui est une image également. On ne donne pas le contre-pied d'une image par une modélisation complexe, mais par une autre image. C'est donc normal que l'exemple du médecin paraisse si simple. Mais il serait hors-sujet de dire qu'elle est simpliste, comme ont pu le dire des intervenants. On ne dit pas d'une image qu'elle est simpliste car c'est le but d'une image que de saisir l'essence d'un concept ou d'une problématique. Je n'ai pas dit de l'image du chien qu'elle était simpliste, et je trouve qu'elle illustre bien le cas général (le plus fréquent à mon avis) où Déméter convient. Et je n'ai donc pas tenté d'élaborer ma propre modélisation du chien, plus complexe, pour aller dans mon sens : cela aurait été hors-sujet.
Vous trouverez facilement sur stack-overflow des contre-images visant à montrer intuitivement qu'on peut trouver de nombreux exemples où Déméter est contre-productif.

Encore une fois, mon article repose sur l'existence de tels cas. C'est une position facile à tenir à mon humble avis. Quant aux cas où Déméter fonctionne bien, je les traite explicitement dans la dernière section "Déméter et encapsulation". Les nombreux intervenants qui ont tenté d'invalider mon article illustrant leurs propos par des cas où on expose des types internes, qui n'ont vocation qu'à être utilisés par l'implémentation, n'ont soit pas compris le point, soit pas lu mon article jusqu'au bout.

-------------

Je trouve dommage que, sur un forum de programmation qui se veut professionnel, lorsqu'un nouveau point de vue est apporté sur un sujet où l'essentiel des publications se contente de vulgariser ce que le lecteur peut lui-même trouver facilement sur le net, on puisse assister à un tel tir de barrage. Avec autant de défauts de lecture évidents, de propos hors-sujet ou déplacés, de jugements sur la personne plutôt que sur les idées pour certains je pense en réalité qu'il s'agit plus d'un effet d'extrémisme idéologique : certains semblent vouer une passion pour certains principes de conception, et il est facile d'être piqué quand on est passionné. Mais la passion est rarement une bonne chose en matière de programmation comme ailleurs. Ne soyez pas esclaves de vos principes. Chaque situation a ses caractéristiques propres, et beaucoup ne peuvent satisfaire simultanément tous ces principes. Connaître et apprendre ce principes est très bien, mais les appliquer a priori, automatiquement, sans réfléchir en premier lieu aux caractéristiques concrètes de chaque situation risque de conduire à des conceptions inadaptées, et souvent excessivement abstraites.

Je clos ici ma participation à cette discussion, et laisse place au ballet fascinant des +1 -1.
Avatar de ternel ternel - Expert éminent sénior http://www.developpez.com
le 29/01/2016 à 15:31
Le problème, c'est que tu ne sembles pas avoir saisi le sens de la Loi de demeter.

Elle dit de ne pas exposer la structure de la classe, pas d'interdire l'accès au contenu sémantique.

D'un autre côté, si ta fonction n'agit que sur un comportement, pourquoi prend-elle en argument un Agent, et pas seulement le comportement?

S'il s'agit comme tu le dis de modéliser les interactions, alors c'est une modélisation, qui n'expose pas le fonctionnement interne du moteur de modélisation.
Par contre, il doit fournir un ensemble de propriété observable (une map d'identifiant vers des valeurs?), et d'unités de prise de décision (des foncteurs?).

Ces agents d'une modélisation n'ont pas à être conçus (de prime abord) comme des médecins, des patients ou des secrétaires.

Je t'invite d'ailleurs à regarder ce qu'est un ECS (entity component system).
On a eu pas mal de discussions à ce sujet, l'an dernier; celle-ci, par exemple.

Quant à l'avis sur nos lectures de ton article, si tu trouves qu'autant de gens n'ont pas compris ton propos, c'est probablement parce qu'il est mal exprimé.
J'ai personnellement trouvé ton article sensationnaliste, c'est à dire affirmatif, avec en guise de preuve des exemples (forcément arbitraires et partiels)
Avatar de koala01 koala01 - Expert éminent sénior http://www.developpez.com
le 29/01/2016 à 16:03
Citation Envoyé par igor_  Voir le message
Si un médecin libéral travaillant avec une secrétaire vous fait la commande d'un logiciel permettant de modéliser et tracer les interactions entre lui, sa secrétaire et le patient lors de la prise de RV, on peut imaginer 2 cas de figure :
- vous faites ce qu'il vous a demandé et vous serez payé.
- vous estimez que, dans le besoin exprimé, seul compte vraiment le point de vue du patient et qu'il faut abstraire le reste. Vous créer donc une interface abstraite offrant au patient tous les services nécessaires à sa prise de rendez-vous, et vous implémentez ces services de manière interne. Vous êtes plutôt fier de votre conception, car vous avez su aller au-delà du besoin exprimé. Lorsque vous montrez votre prototype au médecin, vous commencez par lui vanter les capacités d'adaptation de votre architecture : le patient ne dialoguant qu'avec une interface abstraite (un "cabinet médical"), le logiciel pourra fonctionner avec un ou plusieurs médecins, zéro ou un ou plusieurs secrétaires, et même en l'absence de tout personnel, en prévoyant des comportement par défaut. Tout ce qu'il y aura à faire, c'est d'implémenter de nouveaux comportements. l'interface principale, elle, restera inchangée. Alors le médecin vous répond : "Mais je vous avais pourtant bien dit que ce que nous avions besoin, c'était de modéliser les interactions directes entre moi, ma secrétaire, et le patient. C'est tout-à-fait impossible avec votre application. Vous avez créé un mur qui empêche cela.

Ou la! tu mélange tout là...

Ce n'est pas parce que l'on dit "il ne faut pas que la prise de rendez vous (l'interaction client <-->cabinet) ne passe par la secrétaire, que l'on n'est pas sur d'avoir" que cela ne veut pas dire que le cabinet n'a effectivement pas de secrétaire ni qu'il est impossible de tracer les interaction entre trois acteurs principaux que sont le(s) toubib(s), la (les) secrétaire(s) et le(s) patient(s).

On dit juste que cela doit être transparent au niveau de la prise de rendez-vous (le patient se fout pas mal de savoir s'il parle à la secrétaire, au docteur ou à un robot lorsqu'il prend rendez-vous : ce qu'il veut c'est une date, une heure et... un peu de temps pour s'organiser afin de pouvoir y être) que, s'il y a "autre chose" à faire entre les différentes interaction -- noter l'heure des appels, leur origine, leur destinataires, leur motifs, par exemple -- cela doit se faire de manière automatique (sans que les intervenant n'en aient forcément conscience) et que, si on veut accéder à ces données par la suite, ca doit se faire "par un autre chemin que celui de la prise de rendez-vous"

Ton approche semble partir du principe que l'on va essayer d'accéder à ces données exactement de la même manière que celle utilisée... pour créer ces données. Or, le principe de base est de créer les données d'un coté pour pouvoir les utiliser de l'autre. Même dans un atelier d'ébénisterie, les machines qui permettent de débiter et de préparer les planches sont "physiquement" séparées des établis sur lesquels on les peaufine, on les adapte et on les assemble!

Je trouve dommage que, sur un forum de programmation qui se veut professionnel, lorsqu'un nouveau point de vue est apporté sur un sujet où l'essentiel des publications se contente de vulgariser ce que le lecteur peut lui-même trouver facilement sur le net, on puisse assister à un tel tir de barrage.

Généralement, tu peux te dire que les tirs de barrage, lorsqu'ils viennent de certaines personnes, sont pour le moins justifié
Avec autant de défauts de lecture évidents, de propos hors-sujet ou déplacés, de jugements sur la personne plutôt que sur les idées pour certains je pense en réalité qu'il s'agit plus d'un effet d'extrémisme idéologique : certains semblent vouer une passion pour certains principes de conception, et il est facile d'être piqué quand on est passionné.

Oui, je suis passionné par le sujet et par le développement en C++ dans son ensemble, mais non, je ne suis pas piqué quand je croise quelqu'un qui n'est pas de mon avis : j'essaye de lui expliquer les défauts que je croise dans son cheminement intellectuel.
Mais la passion est rarement une bonne chose en matière de programmation comme ailleurs. Ne soyez pas esclaves de vos principes. Chaque situation a ses caractéristiques propres, et beaucoup ne peuvent satisfaire simultanément tous ces principes. Connaître et apprendre ce principes est très bien, mais les appliquer a priori, automatiquement, sans réfléchir en premier lieu aux caractéristiques concrètes de chaque situation risque de conduire à des conceptions inadaptées, et souvent excessivement abstraites.

Même si je reconnais volontiers que je suis de plus en plus extrémiste au sujet des principes fondamentaux, il ne faut pas croire que je sois l'esclave de mes principes.

J'ai essayé de t'expliquer le fait que tu pars d'une hypothèse dans laquelle tu décide volontairement de ne pas respecter l'ISP, que c'est une très mauvaise idée en soi, mais bon après tout, pourquoi pas

Le problème, c'est que, par la suite, tu nous dis "oui, mais l'ISP n'est pas respecté, et du coup, Déméter ne sert à rien, que ce n'est qu'un leurre" (je reprend tes propres termes)

C'est un peu comme si je disais " je me fous royalement de l'étude de sol, que ma maison sera construite sur du sable, je fais ma maison comme bon me semble, sans creuser de fondations et sans mettre une dalle de béton en dessous" puis que ... j'allais gueuler chez celui qui a dressé le plan parce que ma maison penche comme la tour de Pise. N'aurais tu pas tendance à me dire que "je l'ai bien cherché" j'ai volontairement fait une connerie dans la conception de ma maison, j'en paye forcément le prix à un moment ou à un autre.

Eh bien là, c'est pareil : tu fais volontairement une connerie au début de ton raisonnement, puis tu t'étonne que l'on te dise que ton raisonnement est faux parce que basé sur une hypothèse dénuée de bon sens

Maintenant, je suis peut être moi même victime d'une erreur dans mon propre raisonnement. Peut être que je vais trop loin en disant que tu pars d'une hypothèse dans laquelle tu n'as rien à foutre de l'ISP; Peut être la multiplication des services à laquelle on assiste dans ton raisonnement n'a rien à voir avec le respect de l'ISP D'accord... Prouve moi que je fais erreur!

Prouve moi que l'on ne respecte pas moins bien l'ISP quand on expose 5 fonctions que quand on en expose 3, surtout si les deux fonctions supplémentaire on trait à des données internes différentes.

Démontre moi que le fait d'exposer -- sous une forme ou sous une autre -- tous les services proposés par une donnée interne ne revient pas à bêtement fournir un accesseur dessus.

Après tout, voilà en gros les deux bases du raisonnement que j'ai suivi pour démontrer que ton raisonnement était faux. Invalide ces deux hypothèses (ou ne serait-ce que l'une d'entre-elles), et tu invalidera mon raisonnement. Et si tu y arrives, j'accepterai ton raisonnement ou, du moins, j'arrêterai de le démolir en publique, en plus de te faire mes plus plates excuses
Avatar de mintho carmo mintho carmo - Membre actif http://www.developpez.com
le 29/01/2016 à 17:41
Citation Envoyé par igor_  Voir le message
je pense en réalité qu'il s'agit plus d'un effet d'extrémisme idéologique : certains semblent vouer une passion pour certains principes de conception, et il est facile d'être piqué quand on est passionné.

Et toi, sais tu te remettre en question ? N'es tu pas "passionné" par ton "extrémisme idéologique", au point d'avoir du mal à accepter que tu puisses te tromper, que tu as peut être mal compris la loi de Déméter, que tes exemples ne sont pas pertinents ? Et peut être aussi un peu "piqué" que ton article n'a pas eu les retours que tu aurais voulu...
Avatar de mister3957 mister3957 - Membre éprouvé http://www.developpez.com
le 29/01/2016 à 21:55
Citation Envoyé par igor_  Voir le message
Si un médecin libéral travaillant avec une secrétaire vous fait la commande d'un logiciel permettant de modéliser et tracer les interactions entre lui, sa secrétaire et le patient lors de la prise de RV, on peut imaginer 2 cas de figure :
- vous faites ce qu'il vous a demandé et vous serez payé.
- vous estimez que, dans le besoin exprimé, seul compte vraiment le point de vue du patient et qu'il faut abstraire le reste. Vous créer donc une interface abstraite offrant au patient tous les services nécessaires à sa prise de rendez-vous, et vous implémentez ces services de manière interne. Vous êtes plutôt fier de votre conception, car vous avez su aller au-delà du besoin exprimé. Lorsque vous montrez votre prototype au médecin, vous commencez par lui vanter les capacités d'adaptation de votre architecture : le patient ne dialoguant qu'avec une interface abstraite (un "cabinet médical"), le logiciel pourra fonctionner avec un ou plusieurs médecins, zéro ou un ou plusieurs secrétaires, et même en l'absence de tout personnel, en prévoyant des comportement par défaut. Tout ce qu'il y aura à faire, c'est d'implémenter de nouveaux comportements. l'interface principale, elle, restera inchangée. Alors le médecin vous répond : "Mais je vous avais pourtant bien dit que ce que nous avions besoin, c'était de modéliser les interactions directes entre moi, ma secrétaire, et le patient. C'est tout-à-fait impossible avec votre application. Vous avez créé un mur qui empêche cela.

mister3957, comme beaucoup d'intervenants, vous vous précipitez pour imaginer des besoins. Et si et si et si. Je ne dis pas que vos généralisations et vos abstractions ne sont pas bonnes. Elles sont excellentes et plutôt naturelles. Mais, simplement, elles sont hors sujet ici. Dans mon article, l'exemple du médecin explicite des entités claires, et il exprime clairement la problématique de la prise de RV en termes d'interactions entre ces entités. Vous pouvez proposer toute autre modélisation, abstraite à souhait, aucun souci. Mais c'est alors un autre cas d'étude. Vous pouvez dire que ma modélisation ne peut correspond à aucun problème en pratique et qu'elle ne sert donc à rien, mais c'est une position difficile à tenir : il est plus facile d'affirmer qu'une modélisation peut correspondre à des cas réels que l'inverse. J'ai tenté de précisé un cas réel plus haut.

Enfin, cet exemple du médecin et avant tout une image. Dans mon article, je ne le détaille pas plus que l'exemple du chien qui est une image également. On ne donne pas le contre-pied d'une image par une modélisation complexe, mais par une autre image. C'est donc normal que l'exemple du médecin paraisse si simple. Mais il serait hors-sujet de dire qu'elle est simpliste, comme ont pu le dire des intervenants. On ne dit pas d'une image qu'elle est simpliste car c'est le but d'une image que de saisir l'essence d'un concept ou d'une problématique. Je n'ai pas dit de l'image du chien qu'elle était simpliste, et je trouve qu'elle illustre bien le cas général (le plus fréquent à mon avis) où Déméter convient. Et je n'ai donc pas tenté d'élaborer ma propre modélisation du chien, plus complexe, pour aller dans mon sens : cela aurait été hors-sujet.
Vous trouverez facilement sur stack-overflow des contre-images visant à montrer intuitivement qu'on peut trouver de nombreux exemples où Déméter est contre-productif.

Encore une fois, mon article repose sur l'existence de tels cas. C'est une position facile à tenir à mon humble avis. Quant aux cas où Déméter fonctionne bien, je les traite explicitement dans la dernière section "Déméter et encapsulation". Les nombreux intervenants qui ont tenté d'invalider mon article illustrant leurs propos par des cas où on expose des types internes, qui n'ont vocation qu'à être utilisés par l'implémentation, n'ont soit pas compris le point, soit pas lu mon article jusqu'au bout.

-------------

Je trouve dommage que, sur un forum de programmation qui se veut professionnel, lorsqu'un nouveau point de vue est apporté sur un sujet où l'essentiel des publications se contente de vulgariser ce que le lecteur peut lui-même trouver facilement sur le net, on puisse assister à un tel tir de barrage. Avec autant de défauts de lecture évidents, de propos hors-sujet ou déplacés, de jugements sur la personne plutôt que sur les idées pour certains je pense en réalité qu'il s'agit plus d'un effet d'extrémisme idéologique : certains semblent vouer une passion pour certains principes de conception, et il est facile d'être piqué quand on est passionné. Mais la passion est rarement une bonne chose en matière de programmation comme ailleurs. Ne soyez pas esclaves de vos principes. Chaque situation a ses caractéristiques propres, et beaucoup ne peuvent satisfaire simultanément tous ces principes. Connaître et apprendre ce principes est très bien, mais les appliquer a priori, automatiquement, sans réfléchir en premier lieu aux caractéristiques concrètes de chaque situation risque de conduire à des conceptions inadaptées, et souvent excessivement abstraites.

Je clos ici ma participation à cette discussion, et laisse place au ballet fascinant des +1 -1.

Je ne comprends pas ta réaction, pourquoi tu te sens offensé comme ça, il est où le problème ?

Tu sous entends donc que l'on suive des principes fondamentaux juste comme ça pour se faire plaisir, qu'on travaille juste pour assouvir notre passion de manière égoïste sans penser aux besoins ? En somme que l'on a rien à voir avec des ingénieurs mais qu'on est juste des geeks qui veulent "jouer à l'ordinateur" en oubliant tout le reste à différents termes c'est bien ça ?

Je te trouve très méprisant et carrément anti constructif dans la mesure où le sujet que tu poses et les questions qui vont avec sont excellentes et n'ont pas lieu à un débat mais à une recherche instructive et intellectuelle.

Si tu n'es pas assez ouvert pour recevoir des réponses lorsqu'elles ne collent pas avec ce que tu attends, pourquoi tu t'exposes comme ça sur un forum ? Et bien sûr c'est la faute du forum et des guignoles qui le composent en cherchent des points, un score, une popularité etc.. J'en ai connu des gens qui se sentait au dessus des autres, mais un comportement comme le tiens ça vise le top du top du négatif !

A un moment donné quand tu penses que tout le monde se trompe, il y a des questions à se poser, c'est peut-être pas l'écosystème qui a un problème, mais toi.

Je pose ma question : d'où vient cette frustration ? T'as connu un sale truc récemment ou quelque chose comme ça ?

Ce que tu racontes est tout sauf constructif, ça ne vaut rien à moyen et long termes. Ok on peut être contraint à user de pragmatisme mais pour se faire il faut absolument cette couche d'abstraction qui puisse faire en sorte que ça soit possible, rentable à termes et surtout qui ne soit pas gangreneux, et ça c'est pas la responsabilité du client, c'est la notre en adéquation avec le contexte.

Il n'y a qu'un seul type de motivation qui agirait consciemment comme ça, c'est le genre de fournisseur qui vise la dépendance pour tenir son client par les couilles dans l'idée de le démonter à niveau maintenance. Permets moi d'avoir des doutes quand à ce type d'agissement, des gens qui l'alimentent ainsi que l'intérêt des clients à terme.
Avatar de Luc Hermitte Luc Hermitte - Expert éminent http://www.developpez.com
le 01/02/2016 à 9:18
J'ai survolé les derniers propos et j'ai beaucoup aimé l'exemple de Bousk.

Il y a je pense moyen de mettre de converger -- pas forcément de beaucoup, mais un peu.
Il y a des soucis de fuite d'abstraction quand on parle de médecin, de secrétaire, etc.

Le cabinet va proposer plusieurs services/cas d'utilisation.
- Obtenir/"éditer" un RDV
- consulter
et possiblement d'autres.

On ne va pas contacter le médecin, mais le cabinet. Là, on va être redirigés vers l'interface de prise de RDV. interface fort spécialisée, ISP oblige. Et le cabinet ne va pas forcément faire la chose lui-même, SRP oblige -- surtout si les services se multiplient. Obtenir l'interface de prise de RDV va nous faire passer par la secrétaire présente, ou une ASV chez le véto, ou le toubib s'il est seul, etc. Et on a l'impression que l'on viole Déméter.
La difficulté avec cette loi est d'arriver à l'équilibre entre Déméter et SRP. Si on expose tous les services de tous nos composants via l'objet de haut niveau, on va avoir un soucis côté SRP. Effectivement, il va y avoir un moment où il va falloir admettre que le service est un objet complexe et que l'on demande accès à un service. Mais nommer "getSecrétaire()" ce service sera une fuite d'abstraction dans certains cas, et trop limitatif si cela doit évoluer.
Avatar de koala01 koala01 - Expert éminent sénior http://www.developpez.com
le 02/02/2016 à 19:23
En fait (bien que j'aie l'impression que Igor_ a décidé d'ignorer cette discussion), il serait peut-être temps de revenir aux fondamentaux les plus évidents en se posant la question essentielle : Pourquoi créer une agrégation de données (quel qu'en soit le type)

La réponse à cette question est double :
  1. Pour permettre aux données agrégées de "travailler ensembles" et
  2. pour obtenir un type de donnée qui nous permette de faire bien plus que la somme de ses composants


(en fait, je ne suis pas sur du tout qu'il y ait une si grande différence entre les deux raisons mais bon... )

Vous voulez un exemple simple Pensez à une structure Coordinate2D ou à une structure Date : chaque composant n'est jamais qu'un type primitif "à définir selon nos besoins", éventuellement une énumération (qui n'est rien d'autre qu'une ensemble de valeurs numériques entières, ce qui revient au même), mais, grâce au fait qu'on fait travailler tous les composants ensembles, nous pouvons utiliser l'agrégation dans un domaine tout à fait différent de celui dans lequel nous aurions utilisé notre type primitif.

Bon, d'accord, ces deux types ont sémantique de valeur, ce qui est encore "un peu particulier" par rapport au cas proposé par Igor_, mais le fait reste que l'on obtient quelque chose utilisable dans un référentiel (beaucoup) plus complexe que celui que l'on aurait pu utiliser... avec les trois composants pris séparément.

Le fait est que la sémantique de valeur et la sémantique d'entité n'interviennent absolument pas à ce niveau. Elle définissent les propriétés intrinsèques que l'on peut s'attendre, mais elle ne changent absolument pas le but de la création d'une agrégation.

Si bien que si l'on a -- pour reprendre l'exemple de Igo_ - une classe Savoir et une classe Comportement, le but en agrégeant une donnée de chaque type n'est pas "simplement" de disposer d'un type de donnée qui reprenne les services proposés par la classe Savoir et par la classe Comportement à son compte : le but est de disposer d'un type de donnée qui fournisse des comportements ayant recours aux services proposés par (prafois l'une de ces données, parfois l'autre, parfois) les deux données prises ensemble pour déterminer la logique à mettre en oeuvre.

Il arrive en effet parfois que l'un des services dont on s'attend à disposer de la part de la classe utilisatrice ne soit rien d'autre... qu'un service tout droit issu de l'une des données agrégées, mais, dans la plupart des cas, les services dont on s'attend à disposer utiliseront sans vergogne n'importe quel service proposé par n'importe quelle donnée agrégée dans... à peu près n'importe quelle combinaison.

Cela sous entend que l'on peut très bien avoir accès "ailleurs" aux données de type Savoir ou aux données de type Comportements qui sont agrégées pour obtenir notre type Agent, et que nous pouvons donc -- en cas de besoin, mais en respectant certaines restrictions -- y avoir recours "séparément".

On peut même parfaitement envisager -- si besoin -- d'avoir accès au même instant aux données de type Savoir et aux données de type Comportement s'il se fait que l'on a besoins des services fournis par ces deux types mais que leur utilisation n'entre pas dans le cadre des attributions de la classe Agent.

Quoi qu'il en soit, si on décide de créer une classe d'agrégation et de se contenter d'exposer les différents services proposés par les données agrégées, cela n'a pas beaucoup de sens, entre autres parce que cela viole le SRP et l'ISP.

aussi, quand tu dis
Citation Envoyé par igor_  Voir le message

Mais peut-être qu'en inversant le sens de ma démonstration, ces deux points te sembleront plus clairs :

Situation 1 :
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 
class Agent 
{ 
public: 
   string nom() const { return m_nom; } 
   string affiche() const; 
 
   void demarre() { m_comportement.demarre(); } 
   void arrete() { m_comportement.arrete(); } 
   string descriptionComportement() const { return m_comportement.description(); } 
 
   bool connait(const Fait & fait) const { return m_savoir.connait(fait); } 
   bool peutDeduire(const Fait & fait) const { return m_savoir.peutDeduire(fait); } 
 
private: 
   Comportement m_comportement; 
   Savoir m_savoir; 
   string m_nom; 
};

Je dis : mauvaise compréhension de la notion de service : celui qui va utiliser ta classe Agent se fout pas mal de savoir ce que l'agent connait, la description du comportement qu'il va manipuler en interne ou ce qu'il peut déduire d'une donnée quelconque! Il va invoquer un service spécifique à la classe Agent (obéit ) en lui fournissant toutes les données qui sont nécessaires pour que l'Agent puisse "prendre ses propres décisions", par exemple sous une forme qui serait proche de
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 
class Agent 
{ 
public: 
   string nom() const { return m_nom; } 
   string affiche() const; 
   void reagis(Fait const & fait){ 
       /* tu vois, quand je parle de "n'importe quelle combinaison cohérente" */ 
       if (!(m_savoir.connait(fait)  || m_savoir peutDeduire(fait))){ 
           m_savoir.apprend(fait); 
       } 
       m_comportement. demarre(); 
    } 
private: 
   Comportement m_comportement; 
   Savoir m_savoir; 
   string m_nom; 
};
De cette manière on peut tout à fait s'en foutre de savoir si les classes Savoir ou Comportement sont des classes abstraites, des abstractions ou des types de données réels : on utilise directement les services qu'elles exposent pour permettre à la classe Agent de fournir un service qui est bien plus que l'ensemble des services proposés par Savoir et par Comportement.
Code : Sélectionner tout
1
2
3
4
5
6
 
Ou, pour faire encore plus simple : le code 
Luc.getComportement().demarre(); 
Luc.affiche(); 
gereObstacles(Luc.getComportement());     // n'utilise que Agent::arrete 
afficheConnaissances(Luc.getSavoir());       // n'utilise que Agent::connait et Agent::peutDeduire
devrait non pas se trouver dans une fonction libre quelconque, ni même dans la fonction membre d'une quelconque autre classe utilisatrice de la classe Agent, mais... dans une fonction membre de la classe Agent "himself" (avec les modifications requises pour que cela fonctionne, bien sur )

Alors, bien sur, il faut aussi veiller à ne pas trop malmener le DIP. Mais, si tu crées une classe qui doit permettre d'appliquer DIP à ta classe Agent, cette classe ne devra, de toutes manières, dépendre de rien d'autre que des services (plus ou moins exposés, selon le cas) par ta classe Agent.

Si bien que si tu veux -- ailleurs dans ton code -- appliquer DIP sur des données de type Savoir ou sur des données de type Comportement, il faudra encore une fois veiller à ce que les données de ces deux types utilisées par les différentes instances de la classe Agent soient ... accessibles par "un autre chemin" (un "gestionnaire" -- dieux que je n'aime pas ce terme -- quelconque )
Avatar de bacelar bacelar - Expert éminent http://www.developpez.com
le 03/02/2016 à 14:39
Ce "débat" sur les objets composites me fait penser à la solution proposer par COM.
Solution qui permettait d'avoir des objets sachant qu'ils font partie d'un "ensemble" et de pouvoir déléguer tout ou partie des fonctionnalités vers cet ensemble.
Ensemble qui lui-même pouvait demander à d'autres éléments de cette ensemble de faire les actions nécessaires.
C'est une approche plus composant que purement objet.
Offres d'emploi IT
Consultant fonctionnel banque - junior H/F
Sogeti France - Ile de France - Issy-les-Moulineaux (92130)
Architecte .net / Sitecore
AMETIX - Ile de France - Paris (75000)
Architecte fonctionnel sales & supply H/F
MENWAY TALENTS - Ile de France - Paris (75000)

Voir plus d'offres Voir la carte des offres IT
Contacter le responsable de la rubrique Accueil