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

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

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

Le , par 3DArchi

43PARTAGES

0  0 
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 ?

Une erreur dans cette actualité ? Signalez-nous-la !

Avatar de mintho carmo
Membre éclairé https://www.developpez.com
Le 27/01/2016 à 12:40
@igor_

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

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

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

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

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

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

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

Maintenant, on pourrait discuter sur l'intérêt d'avoir du code de qualité ou non. Dans certains cas (prototypage ?), il est possible que la qualité du code n'est pas un point critique. Mais pour du code pro "standard", qui sera maintenu pendant des années, la qualité du code (et d'une bonne conception) est très critique. Et le respect de la loi de Déméter n'est pas sans intérêt dans ce cas.
4  0 
Avatar de koala01
Expert éminent sénior https://www.developpez.com
Le 27/01/2016 à 19:52
Citation Envoyé par igor_ Voir le message
L'industriel, utilisateur final, se "tamponne" de beaucoup de choses, en particulier des aspects techniques, et il a ses raisons.
L'équipe de développement qui conçoit et maintient le système n'a pas les mêmes contraintes.
Si l'architecture logicielle du coeur d'un simulateur pouvait se réduire à un simple vector listant des composants, le développement informatique serait une tâche bien plus simple qu'il ne l'ait.

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

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

Je ne remets ici absolument pas en question la qualité du travail effectué pour fournir ces bibliothèques, et j'apprécie énormément certaines d'entre-elles. Mais, de manière générale, la conception d'une bibliothèque d'IHM n'est vraiment pas le meilleur exemple à prendre

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

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

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

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

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

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

Car, tout ce que l'on attend de la part des agent est... de réagir (correctement) aux ordres que l'on est susceptibles de leur donner. Et donc, les seuls services "dignes" d'être exposés au niveau des agents sont... les ordres auxquels ils doivent pouvoir réagir et éventuellement quelques "aides à la décision" permettant à l'utilisateur de choisir de donner (ou non) un ordre particulier. Et l'utilisateur d'un agent particulier n'a ABSOLUMENT PAS A SAVOIR DE QUOI L'AGENT QU'IL UTILISE EST COMPOSE;

Attention : je ne détaille pas les composantes dont je parle dans mon article, et c'est voulu. Il n'y a aucune raison de penser que, dans l'implémentation, ces types sont des types concrets. Cela sort du cadre du sujet de la loi de Déméter, mais il faut imaginer par exemple que Comportement peut être un type abstrait, géré par un unique_ptr. Cela permet à un Agent de ne pas avoir l'état de son Comportement "figé" (comme tu mentionnes). Les comportements spécifiques des Agents peuvent ainsi être implémentés de manière différente, en toute transparence pour le code de Agent ET client, qui ne manipulent qu'une composante abstraite.
Ces considérations sont hors-sujet, mais je tenais à rectifier ce point.
Cela n'a rien à voir !!! A partir du moment où tu considère que ton agent doit exposer la (quasi) totalité des services fournis par ses composants, tu fais purement et simplement fausse route : l'agent n'a à exposer que les services qu'on attend de sa part!

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

Si on a décidé de regrouper plusieurs composants au niveau de la classe Agent, ce n'est que parce que l'on s'est bel et bien rendu compte que les différents composants devaient être utilisés conjointement à certaines occasions. Et les services exposés par ta classe Agent ne correspondent en réalité qu'à ... ces "occasions particulières" dans lesquelles les différents composants doivent être utilisés conjointement
4  0 
Avatar de Bousk
Rédacteur/Modérateur https://www.developpez.com
Le 28/01/2016 à 10:17
Citation Envoyé par igor_ Voir le message
L'industriel, utilisateur final, se "tamponne" de beaucoup de choses, en particulier des aspects techniques, et il a ses raisons.
L'équipe de développement qui conçoit et maintient le système n'a pas les mêmes contraintes.
On peut travailler dans la même équipe, sur des pans différents. Je n'en suis pas moins un utilisateur de ta classe, et son implémentation je m'en cogne tout autant que ce que tu appelles "utlisateur final".

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

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

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

Et si demain le docteur décide de changer quelque chose, ajouter/supprimer une secrétaire, que les agendas de rendez-vous sont maintenant électroniques, les bureaux, ... d'un côté tu dois reprendre ton code pour coller, de l'autre osef et seuls ceux qui implémentent ce flow auront à changer quelque chose
4  0 
Avatar de Luc Hermitte
Expert éminent sénior https://www.developpez.com
Le 26/01/2016 à 18:18
Déméter s'inscrit dans la continuité de l'abstraction -- c'est l'abstraction qui cache les détails et stabilise les interfaces, l'encapsulation assure les invariants.
Quand ton agent expose 150 propriétés, et qu'il faut passer par ces dernières pour le manipuler, l'abstraction a fuit.

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

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

PS: je ne vois pas le rapport avec la sémantique de déplacement, ni même avec a*X + Y, expressions templates ou pas. Je ne demande certainement pas à ma matrice une référence interne pour aller taper dedans.
3  0 
Avatar de koala01
Expert éminent sénior https://www.developpez.com
Le 26/01/2016 à 20:09
Ohhh, il y a tant à dire sur ton article, je vais encore une fois exploser la base de données

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

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

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

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

(emphasis is mine )
Non!

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

Bon, je vais m'arrêter là, mais je ne dirais qu'une seule chose : ton approche est loin d'être assez complète et "conceptuellement cohérente" que pour que l'on puisse prendre tes conclusions au sérieux
3  0 
Avatar de ternel
Expert éminent sénior https://www.developpez.com
Le 28/01/2016 à 13:45
Par contre, tu perds la possibilité d'avoir un agent qui choisit son comportement parmi plusieurs, en fonction de ce qu'il estime être le besoin.
Tu perds aussi la possibilité d'avoir un Agent qui implémente son propre comportement.
3  0 
Avatar de Emmanuel Deloget
Expert confirmé https://www.developpez.com
Le 16/10/2009 à 4:56
Citation Envoyé par Lavock Voir le message
:o ; On peux aussi se mettre un doigt dans le *** et chanter la reine Margot en programmant....
Là, tes collègues risquent de dire à ton chef que tu les perturbe un peu pendant leur travail. Mais c'est à tenter...

Citation Envoyé par Lavock Voir le message

Quand on programme en c++, il y a globalement deux objectifs : l'efficacité et la clarté.
Voui. Mais dison plutôt : la clarté et l'efficacité.

Citation Envoyé par Lavock Voir le message

En reprenant l'exemple de deloget; oui il est possible de faire une combineinthesamedirection(); qui, si on veut être performant, sera un vieille inline... mais bonjour la clarté ! On va se retrouver à faire 40 fonctions inlines, qui pourriront une potentiel doc, juste pour respecter cette loi...

--> Moins de clarté, pas plus de perf = Poubelle.
Pas d'accord. La clarté dépends uniquement du bon choix des noms de méthode. Une méthode correctement nommé est claire. Un identifiant qui fait entre 5 et 25 caractères est largement lisible par tout programmeur qui se respecte.

Ensuite : pas d'accord non plus. Non, on ne va pas se trouver à faire 40 inline - ça n'aurait pas de sens. Si on fait 40 inline dans une classe, alors il y a un vrai problème de non respect du SRP. Donc on ne va pas pourrir les docs, qui ne sont pas potentielles d'ailleurs, mais contractuellement exigée par la plupart des clients sensés.

Donc au final : pas d'accord avec la conclusion. Ma conclusion est "plus de clarté, pas plus de perf -> on garde".

Citation Envoyé par Lavock Voir le message

Déclarer un getters en const Type& veut bien dire ce que ça veut dire : On retourne un objet dont on pourra se servir => conférer à cette doc. Ca s'appelle du partitionnement, de la décentralisation en politique; de la sous-traitance en industrielle. (puis commencé à faire comme si y avait pas de sous-traitance, c'est malsain).

Et puis avec la venue des rrefs dans C++0x, je la trouve vraiment contre productive (en tout cas en c++).
Je vois pas trop l'impact des rrefs la dedans.

Citation Envoyé par Lavock Voir le message

Bon, après, c'est comme pour une bonne page internet. Il y a deux choses qui produise un manque de clarté : la profondeur et la largeur. Si cette loi pose un problème sur la largeur; il faut tâcher aussi de ne pas s'embourber dans du :
Code : Sélectionner tout
 param.getter().getter().getter().getter().method();
Seulement, y a pas vraiment de règle pour ça. Demeter est certes un pattern intéressant, mais pas une loi !
C'est une loi de style. La taxonomie des loi/principes/patterns/... n'impose pas qu'une loi de style doit être nécessairement suivie. Chacun voit midi à sa porte.
2  0 
Avatar de 3DArchi
Rédacteur https://www.developpez.com
Le 16/10/2009 à 12:39
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.
2  0 
Avatar de Emmanuel Deloget
Expert confirmé https://www.developpez.com
Le 20/10/2009 à 14:35
Citation Envoyé par Jean-Marc.Bourguet Voir le message
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).
La règle de "pas de référence circulaire au niveau des classes" a autant de sens qu'au niveau du package. En fait, c'est un corolaire du principe d'inversion de dépendance - si une référence circulaire existe, alors l'inversion de dépendance n'est pas possible - et une conséquence directe du principe de responsabilité - si la classe A dépends de B, alors B est une responsabilité de A. Si B dépends aussi de A, alors A est une responsabilité de B. Si les deux conditions sont réunies, ni B ni A ne peuvent avoir d'autre responsabilité selon le principe de responsabilité unique, et logiquement, le code ne fait rien. Pas d'une utilité fameuse tout ça...

Ce n'est pas tout. Les références circulaires sont aussi préjudiciables à l'application du principe ouvert/fermé - dans le sens ou il devient difficile voire impossible d'étendre du code sans modifier le code existant dès lors que ce qu'on souhaite étendre s'auto-référence.

Trois des principaux principes de conception objet qui sont malmenés sur les 5 les plus utiles, je trouve que c'est un bon chiffre qui nécessite qu'on s'attarde un peu à ce point particulier

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.
setPosition() est un accesseur, tandis que moveTo() est bel et bien un traitement. Dans les faits, les deux font la même chose, pourtant l'un a du sens, l'autre pas tellement. L'abstraction apportée par le nom moveTo() est plus intéressante au point de vue architecture que celle apportée par setPosition(). J'accepte donc moveTo(), mais pas setPosition().
2  0 
Avatar de koala01
Expert éminent sénior https://www.developpez.com
Le 27/01/2016 à 2:11
En quoi ai-je été discourtois dans mon message j'ai peut être été brutal dans le choix de mes termes, mais j'ai visiblement atteint mon objectif : te bousculer assez pour te forcer à réagir.

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

Et si tu trouves présomptueux de ma part de dire "je vais encore une fois exploser la base de données", saches qu'il 'est déjà arrivé plus souvent qu'à mon tour de vouloir poster une réponse et d'obtenir un message du genre "désolé, mais la taille des message est limitée à XXXXX (65 535, si mes souvenirs sont bons) caractères et votre message en fait (heu... entre "quelques uns" et "beaucoup" de plus). Cette phrase n'était donc rien de plus qu'un clin d'oeil au fait que je risquais -- encore une fois -- de vouloir écrire un roman que se serveur refuserait d'enregistrer pour cause de "nombre de caractères excessifs" .
2  0